ASP.NET MVC Done Right: ValidateModelState loses Query Parameters using ValidateModelStateAttribute

StackOverflow https://stackoverflow.com/questions/17094829

  •  31-05-2022
  •  | 
  •  

Pergunta

using Ben Foster Automatic Validate Model State I have this code :

[ImportModelStateFromTempData]
public ActionResult Edit(int id, int otherProperty) {
    // ...
    return View()
}

[HttpPost,ValidateModelState]
public ActionResult Edit(int id, PaymentOrderCreateUpdateCommand order) {
    // ...
    return RedirectToAction("Index", new {otherProperty = order.otherProperty}
}

Being on the url :

/Edit/5?otherProperty=4

If a fill a some form that post to Edit [HttpPost] action with an invalid model state, the ValidateModelState do it's thing and returns the request to

/Edit/5 

problem is the ?otherProperty=4 needed for that view to be generated is lost in the Redirect.

Anyone knows of a way to modify the automatic model state validation attribute so it includes the query parameters on it ?

for completion on the question I add the ValidateModelStateAttribute class :

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateModelStateAttribute : ModelStateTempDataTransfer {
    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        if (!filterContext.Controller.ViewData.ModelState.IsValid) {
            if (filterContext.HttpContext.Request.IsAjaxRequest()) {
                ProcessAjax(filterContext);
            } else {
                ProcessNormal(filterContext);
            }
        }

        base.OnActionExecuting(filterContext);
    }

    protected virtual void ProcessNormal(ActionExecutingContext filterContext) {
        // Export ModelState to TempData so it's available on next request
        ExportModelStateToTempData(filterContext);

        // redirect back to GET action
        filterContext.Result = new RedirectToRouteResult(filterContext.RouteData.Values);
    }

    protected virtual void ProcessAjax(ActionExecutingContext filterContext) {

        var errors = filterContext.Controller.ViewData.ModelState.ToSerializableDictionary();
        var json = new JavaScriptSerializer().Serialize(errors);
        // send 400 status code (Bad Request)
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
        filterContext.HttpContext.Response.StatusDescription = "Invalid Model State";
        filterContext.Result = new ContentResult() { Content = json };
    }
}
Foi útil?

Solução

adding this to the ProcessNormal :

// add query string values.
var qs = filterContext.HttpContext.Request.QueryString;
foreach (string key in qs.Keys) {
    filterContext.RouteData.Values.Add(key, qs.Get(key));
}

looks like it does the trick. I'm not sure if this is ok or can be a problem tho.

Outras dicas

You could also use RouteValues instead of query parameters, and change your routes accordingly. For example:

routes.MapRoute(
            name: "",
            url: "{controller}/{action}/{id}/{p1}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional,p1 = UrlParameter.Optional }
        );

That way you can recieve id and p1 parameters. Using your tweak to [ValidateModelState] it's important to remember that you need to pass query parameters with routeArgument. If you use @Html.HiddenFor, they won't be in query string.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top