locked
MVC 4 validation attribute filter RRS feed

  • Question

  • User-1119538336 posted

    I am implementing web api's in MVC 4. I have created custom action filter for validation. Everything is working clean and nice, except the validation error message. I want validation message 

    HTTP/1.1 400 Bad Request
    Content-Type: application/json; charset=utf-8
    Date: Tue, 16 Jul 2013 21:02:29 GMT
    Content-Length: 331
    
    {
      "Message": "The request is invalid.",
      "ModelState": {
        "product": [
          "Required property 'Name' not found in JSON. Path '', line 1, position 17."
        ],
        "product.Name": [
          "The Name field is required."
        ],
        "product.Weight": [
          "The field Weight must be between 0 and 999."
        ]
      }
    }

    Where as mine is coming like 

    {
      "book1.Name": {
        "_errors": [
          {
            "<Exception>k__BackingField": null,
            "<ErrorMessage>k__BackingField": "The Name field is required."
          }
        ],
        "<Value>k__BackingField": null
      },
      "book1.Price": {
        "_errors": [
          {
            "<Exception>k__BackingField": null,
            "<ErrorMessage>k__BackingField": "The field Price must be between 0 and 100."
          }
        ],
        "<Value>k__BackingField": null
      }
    }

    I refereed to the link for https://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api . Its really good content. but anly difference is the validation response message.

    Here is the validation filter 

    public class ValidateBookFilter: System.Web.Http.Filters.ActionFilterAttribute,IActionFilter
        {
            public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
            {
                if (actionContext.ModelState.IsValid == false)
                {
                    actionContext.Response = actionContext.Request.CreateResponse(
                        HttpStatusCode.BadRequest, actionContext.ModelState);
                }
            }
        }

    Tuesday, November 15, 2016 2:39 PM

Answers

  • User1237060513 posted

    Hi Amol,  You have to CreateErrorResponseinstead of CreateResponse

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
    if (actionContext.ModelState.IsValid == false)
    {
    actionContext.Response = actionContext.Request.CreateErrorResponse(
    HttpStatusCode.BadRequest, actionContext.ModelState);
    }
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, December 9, 2016 9:04 AM

All replies

  • User-6180675 posted

    Can you post where are you using the filter attribute in your code to capture the validation message , you are supposed to register the filter in the webapiconfig.cs  file like below if you want to use it for all http request .

    config.Filters.Add(new ValidateBookFilterAttribute());

    Also the naming convention should be ValidateBookFilterAttribute for your action filter.

    If you want to use it for only a specific controller/action then you can use the attribute like below

    public class BooksController : ApiController
    {
        [ValidateBookFilter]
        public HttpResponseMessage Post(Book book)
        {
            // ...
        }
    }

    Tuesday, November 15, 2016 3:30 PM
  • User-1119538336 posted

    Thanks Nilishere for the reply, 

    1.  ValidateBookFilterAttribute - I have corrected naming convention.

    2. Filter registration - I have registered it into FilterConfig.cs as 

    public class FilterConfig
        {
            public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                filters.Add(new HandleErrorAttribute());
            }
    
            public static void RegisterHttpFilters(HttpFilterCollection filters)
            {
                filters.Add(new ValidateBookFilterAttribute());
            }
        }

    3. As you suggested to register it into webconfig.cs gives same result. 

    public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{action}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
            }
    
            public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                filters.Add(new HandleErrorAttribute());
            }
    
            public static void RegisterHttpFilters(HttpFilterCollection filters)
            {
                filters.Add(new ValidateBookFilterAttribute());
            }
        }

    4. I am facing problem with the response creation, I want it in proper format as discussed in above problem.

    Below is my book class

    public class Book
        {
            public int Id { get; set; }  
          
            [Required]
            public string Name { get; set; }  
          
            [Range(0, 100)]
            public decimal Price { get; set; }
    
            public string Self
            {
                get { return string.Format(CultureInfo.CurrentCulture, "api/books/{0}", this.Id); }
                set { }
            }
        }

    Thanks

    Wednesday, November 16, 2016 6:05 AM
  • User-2057865890 posted

    Hi AmolHegana,

    You could try removing RegisterGlobalFilters and RegisterHttpFilters and changing Register like this.

    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Filters.Add(new ValidateBookFilterAttribute());
        config.Routes.MapHttpRoute(
             name: "DefaultApi",
             routeTemplate: "api/{controller}/{action}/{id}",
             defaults: new { id = RouteParameter.Optional }
       );
    }

    reference: https://forums.asp.net/t/2106421.aspx 

    Best Regards,

    Chris

    Wednesday, November 16, 2016 9:57 AM
  • User-1119538336 posted

    Thanks for the reply Chris Zhao.

    I am developing API's in MVC 4. It doesn't has Config.MapHttpAttributeRoutes() method.  One more thing, I don't have problem with registering filters. I am facing problems for response i am getting from ActionFilter. Below is the response for Post request

    {
      "book1.Name": {
        "_errors": [
          {
            "<Exception>k__BackingField": null,
            "<ErrorMessage>k__BackingField": "The Name field is required."
          }
        ],
        "<Value>k__BackingField": null
      },
      "book1.Price": {
        "_errors": [
          {
            "<Exception>k__BackingField": null,
            "<ErrorMessage>k__BackingField": "The field Price must be between 0 and 100."
          },
          {
            "<Exception>k__BackingField": null,
            "<ErrorMessage>k__BackingField": "Invalid content!!!."
          }
        ],
        "<Value>k__BackingField": null
      }
    }

    Where as i am expecting it in format like.

    HTTP/1.1 400 Bad Request
    Content-Type: application/json; charset=utf-8
    Date: Tue, 16 Jul 2016 21:02:29 GMT
    Content-Length: 331
    
    {
      "Message": "The request is invalid.",
      "ModelState": {
        "book": [
          "Required property 'Name' not found in JSON. Path '', line 1, position 17."
        ],
        "book.Name": [
          "The Name field is required."
        ],
        "product.Price": [
          "The field Price must be between 0 and 999."
        ]
      }
    }

    I don't want  <ErrorMessage>k__BackingField, <Exception>k__BackingField, Instead i want proper caption so that client can easily use it.

    Here is my action filter's onexecuting method

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
            {
                if (actionContext.ModelState.IsValid == false)
                {
                    actionContext.Response = actionContext.Request.CreateResponse(
                       HttpStatusCode.BadRequest, actionContext.ModelState, new System.Net.Http.Formatting.JsonMediaTypeFormatter()); ;                
                }
            }

     

    Thanks !!

    Wednesday, November 16, 2016 10:27 AM
  • User-2057865890 posted

    Hi AmolHegana,

    Can you share a full repro project that reproduces the issue and share it here?

    Best Regards,

    Chris

    Wednesday, November 30, 2016 3:11 AM
  • User-1119538336 posted

    Hi Chris,

    Thanks for your help, I have resolved the issue, it was just changing the way to create the response. I was using CreateResponse where as i should have used CreateErrorResponse.

    All other things as correct and i got the expected result.

    Regards,

    -Amol Hegana

    Wednesday, November 30, 2016 6:10 AM
  • User1237060513 posted

    Hi Amol,  You have to CreateErrorResponseinstead of CreateResponse

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
    if (actionContext.ModelState.IsValid == false)
    {
    actionContext.Response = actionContext.Request.CreateErrorResponse(
    HttpStatusCode.BadRequest, actionContext.ModelState);
    }
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, December 9, 2016 9:04 AM