Last active
April 23, 2025 11:00
-
-
Save Yaevh/e87f682a3c3ac35d1504c068c9f5e8ab to your computer and use it in GitHub Desktop.
Serializing ModelState in TempData to persist in on redirect in POST-REDIRECT-GET pattern in ASP.NET Core 3.1 Razor Pages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // inspired by https://andrewlock.net/post-redirect-get-using-tempdata-in-asp-net-core/ | |
| // usage: in Startup.ConfigureServices() add `services.AddRazorPages().AddMvcOptions(options => options.Filters.Add<SerializeModelStatePageFilter>());` | |
| using Microsoft.AspNetCore.Mvc; | |
| using Microsoft.AspNetCore.Mvc.Filters; | |
| using Microsoft.AspNetCore.Mvc.ModelBinding; | |
| using Microsoft.AspNetCore.Mvc.RazorPages; | |
| using Microsoft.AspNetCore.Mvc.ViewFeatures; | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Threading.Tasks; | |
| namespace Szlem.AspNetCore.Infrastructure | |
| { | |
| public class SerializeModelStatePageFilter : IPageFilter | |
| { | |
| public class ModelStateTransferValue | |
| { | |
| public string Key { get; set; } | |
| public string AttemptedValue { get; set; } | |
| public object RawValue { get; set; } | |
| public ICollection<string> ErrorMessages { get; set; } = new List<string>(); | |
| } | |
| public void OnPageHandlerSelected(PageHandlerSelectedContext context) | |
| { | |
| if (!(context.HandlerInstance is PageModel page)) | |
| return; | |
| var serializedModelState = page.TempData[nameof(SerializeModelStatePageFilter)] as string; | |
| if (serializedModelState.IsNullOrEmpty()) | |
| return; | |
| var modelState = DeserializeModelState(serializedModelState); | |
| page.ModelState.Merge(modelState); | |
| } | |
| public void OnPageHandlerExecuting(PageHandlerExecutingContext context) { } | |
| public void OnPageHandlerExecuted(PageHandlerExecutedContext context) | |
| { | |
| if (!(context.HandlerInstance is PageModel page)) | |
| return; | |
| if (page.ModelState.IsValid) | |
| return; | |
| if (context.Result is IKeepTempDataResult) | |
| { | |
| var modelState = SerializeModelState(page.ModelState); | |
| page.TempData[nameof(SerializeModelStatePageFilter)] = modelState; | |
| } | |
| } | |
| private static string SerializeModelState(ModelStateDictionary modelState) | |
| { | |
| var errorList = modelState | |
| .Select(kvp => new ModelStateTransferValue | |
| { | |
| Key = kvp.Key, | |
| AttemptedValue = kvp.Value.AttemptedValue, | |
| RawValue = kvp.Value.RawValue, | |
| ErrorMessages = kvp.Value.Errors.Select(err => err.ErrorMessage).ToList(), | |
| }); | |
| return System.Text.Json.JsonSerializer.Serialize(errorList); | |
| } | |
| private static ModelStateDictionary DeserializeModelState(string serialisedErrorList) | |
| { | |
| var errorList = System.Text.Json.JsonSerializer.Deserialize<List<ModelStateTransferValue>>(serialisedErrorList); | |
| var modelState = new ModelStateDictionary(); | |
| foreach (var item in errorList) | |
| { | |
| modelState.SetModelValue(item.Key, item.RawValue, item.AttemptedValue); | |
| foreach (var error in item.ErrorMessages) | |
| modelState.AddModelError(item.Key, error); | |
| } | |
| return modelState; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks!