Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconsider Validation HTTP Status Code #6145

Closed
khalidabuhakmeh opened this issue Dec 27, 2018 · 13 comments
Closed

Reconsider Validation HTTP Status Code #6145

khalidabuhakmeh opened this issue Dec 27, 2018 · 13 comments
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates enhancement This issue represents an ask for new feature or an enhancement to an existing one help wanted Up for grabs. We would accept a PR to help resolve this issue
Milestone

Comments

@khalidabuhakmeh
Copy link

Currently the validation pipeline in ASP.NET Core returns a HTTP Status code of 400 which means BadRequest.

httpstatuses.com describes a status code of 400 as the following:

400 BAD REQUEST

The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

I believe the validation pipeline should return a 422 which is Unprocessable Entity.

422 UNPROCESSABLE ENTITY

The server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.

It should be assumed that if the ASP.NET Core pipeline has a chance to get through model binding and run validators, it means the syntax of the request was valid (especially around an API call).

This change would keep the HTTP status code in the family of 4xx error codes.

@rynowak
Copy link
Member

rynowak commented Dec 28, 2018

This comes up from time to time, different people prefer different things. We're interested in ways to make this super easy for you to configure.

@Eilon Eilon added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Jan 2, 2019
@khellang
Copy link
Member

khellang commented Jan 3, 2019

I'm assuming you're referring to the automatic conversion of validation errors into an IActionResult that was introduced with the API conventions stuff? I.e. the ModelStateInvalidFilter?

The InvalidModelStateResponseFactory API was added specifically for this reason, because I moaned about it over a year ago:

https://github.com/aspnet/AspNetCore/blob/afb92018f07bf81c7be092ce93ae36cf1aca5365/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApiBehaviorOptions.cs#L21-L29

It's easy enough to customize it, but it could always be improved. Writing your own quick extension method would be trivial.

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        // Do whatever you want in here, i.e. return an IActionResult or throw an exception.
    };
});

The default implementation looks like this:

https://github.com/aspnet/AspNetCore/blob/708dc5cb5abd1fe0aa409f355b9e0cea8f6846d4/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApiBehaviorOptionsSetup.cs#L98-L103

You could swap BadRequestObjectResult with UnprocessableEntityObjectResult and be done 😄

Otherwise, if you're doing it manually, you can return whatever status code you want. I even added UnprocessableEntityResult, UnprocessableEntityObjectResult and ControllerBase.UnprocessableEntity for convenience.

@RehanSaeed
Copy link
Contributor

RehanSaeed commented Jan 3, 2019

@khellang is more of an expert on this (he wrote the code) and can confirm this but I think in 2.2 the default API behaviour is to use the new ProblemDetails response object which has a different InvalidModelStateResponseFactory method (link to full code shown below):

internal static IActionResult ProblemDetailsInvalidModelStateResponse(ActionContext context)
{
    var problemDetails = new ValidationProblemDetails(context.ModelState)
    {
        Status = StatusCodes.Status400BadRequest,
    };

    ProblemDetailsClientErrorFactory.SetTraceId(context, problemDetails);

    var result = new BadRequestObjectResult(problemDetails);

    result.ContentTypes.Add("application/problem+json");
    result.ContentTypes.Add("application/problem+xml");

    return result;
}

So, as well as swapping BadRequestObjectResult with UnprocessableEntityObjectResult, you'd also change the status code in the ValidationProblemDetails object.

@RehanSaeed
Copy link
Contributor

RehanSaeed commented Jan 3, 2019

With the above said, I still think there is a case for changing the default to 422 because it is more 'correct'. Using the compatibility version method would be the way to go to do it:

services.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)

@khellang
Copy link
Member

khellang commented Jan 3, 2019

To be honest, I think the explicit status code here should be removed:

https://github.com/aspnet/AspNetCore/blob/afb92018f07bf81c7be092ce93ae36cf1aca5365/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApiBehaviorOptionsSetup.cs#L108-L111

As MVC now sets the ProblemDetails status code if none has been specified, based on the status code of the result itself:

https://github.com/aspnet/AspNetCore/blob/afb92018f07bf81c7be092ce93ae36cf1aca5365/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ObjectResult.cs#L61-L64

@mkArtakMSFT
Copy link
Member

@khellang, would you like to send us a PR for this?

@mkArtakMSFT mkArtakMSFT added 1 - Ready enhancement This issue represents an ask for new feature or an enhancement to an existing one help wanted Up for grabs. We would accept a PR to help resolve this issue labels Feb 7, 2019
@mkArtakMSFT mkArtakMSFT added this to the 3.0.0-preview4 milestone Feb 7, 2019
@khellang
Copy link
Member

khellang commented Feb 8, 2019

I'll see if I can squeeze it in. Is there any way this can be assigned to me?

@mkArtakMSFT mkArtakMSFT modified the milestones: 3.0.0-preview4, Backlog Feb 22, 2019
@mkArtakMSFT
Copy link
Member

@khellang, feel free to submit a PR and we'll consider it.

@mkArtakMSFT
Copy link
Member

@khellang, and it isn't really possible to assign issues to non-org members.

@khellang
Copy link
Member

it isn't really possible to assign issues to non-org members.

Yes, it is. I've been assigned multiple issues in corefx/coreclr 😊

https://help.github.com/en/articles/adding-outside-collaborators-to-repositories-in-your-organization

@mkArtakMSFT
Copy link
Member

Thanks for contacting us. We're closing this issue as there was no community involvement here for quite a while now.
And as per an earlier response, this is a breaking change we can't afford. And the scenario is already possible to be done by customers.

@khellang
Copy link
Member

FYI; httpbis is working on a new HTTP Semantics RFC, obsoleting the current HTTP/1.1 RFCs. One of the interesting things in there is that they're pulling 422 Unprocessable Entity from WebDAV and changing the reason phrase to Unprocessable Payload, with the following semantics:

The 422 (Unprocessable Payload) status code indicates that the server understands the content type of the request payload (hence a 415 (Unsupported Media Type) status code is inappropriate), and the syntax of the request payload is correct, but was unable to process the contained instructions. For example, this status code can be sent if an XML request payload contains well-formed (i.e., syntactically correct), but semantically erroneous XML instructions.

From the draft.

As @rynowak stated in aspnet/Mvc#6789 (comment):

Some people are bothered by the fact that 422 is defined by the WebDAV RFC and it's not mentioned at all in RFC7231 which came 7 years later.

So at least that won't be an issue anymore (assuming the draft is approved) 😄

@rynowak
Copy link
Member

rynowak commented Nov 15, 2019

For example, this status code can be sent if an XML request payload contains well-formed (i.e., syntactically correct), but semantically erroneous XML instructions.

I really wish that they would come out and say - this is for communicating validation/business-rule errors. HTTP is apparently a layer 7 protocol, but you wouldn't get that impression from reading the spec 😢

Using phrases like "contained instructions" doesn't match my mental model of what web servers/frameworks do, and is very hard to reconcile with the semantics of REST. /rant

That said, I look forward to seeing this clarified - I still think the spec is pretty unclear, but it's like a 5/10 in unclearness instead of a 7/10.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates enhancement This issue represents an ask for new feature or an enhancement to an existing one help wanted Up for grabs. We would accept a PR to help resolve this issue
Projects
None yet
Development

No branches or pull requests

7 participants