-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add helper methods on ControllerBase to return ProblemDetails #12298
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
Conversation
Status = StatusCodes.Status400BadRequest, | ||
}; | ||
|
||
if (options.ClientErrorMapping.TryGetValue(400, out var clientErrorData)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the new bit. ValidationProblemDetails returned by the ModelStateInvalidFilter should now have a "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1"
(or whatever Link the user configured).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized I'm missing a test to verify we use the user configured value. I'll add one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oke
options.InvalidModelStateResponseFactory = context => | ||
{ | ||
var result = (BadRequestObjectResult)previous(context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test was really janky. This broke because it expected a vanilla dictionary but now gets a ProblemDetails. We wouldn't recommend users to write something like this.
/cc @khellang |
|
||
if (problemDetails.Title is null || problemDetails.Type is null) | ||
{ | ||
var options = HttpContext.RequestServices.GetRequiredService<IOptions<ApiBehaviorOptions>>().Value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little anxious about putting a service here without a way to mock it. I don't have a great solution other than doing things like making this a filter.
Most of the other places we interact with services, you can mock them easily. https://github.com/aspnet/AspNetCore/blob/f7e3eaa7e324b99aeb496a68414cd093acaae802/src/Mvc/Mvc.Core/src/ControllerBase.cs#L171
Does it seem reasonable to make a new service ProblemDetailsFactory
, and then add that as a property, and then call it from here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
@@ -1849,6 +1898,8 @@ public virtual ActionResult ValidationProblem([ActionResultObjectValue] ModelSta | |||
} | |||
|
|||
var validationProblem = new ValidationProblemDetails(modelStateDictionary); | |||
ApplyProblemDetailsDefaults(validationProblem, statusCode: 400); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this broken today? We don't apply the defaults?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup
string instance = null, | ||
string title = null, | ||
string type = null, | ||
[ActionResultObjectValue] ModelStateDictionary modelStateDictionary = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this as a parameter? is there really a use case for passing it in explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have this for BadObjectResult
and another ValidationProblem
. Doesn't seem like it would hurt.
validationProblem.Title = title; | ||
} | ||
|
||
ApplyProblemDetailsDefaults(validationProblem, statusCode: 400); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the people that really want this to be a 422 - what do they do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ValidationProblem
returns the non-specific ActionResult
return type. We expect you to override and copy this block of code if you'd like it to get this behavior. It's a few lines of code to copy.
var controller = new TestableController | ||
{ | ||
ControllerContext = context, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See I don't feel like this amount of setup is reasonable for someone who wants to unit test a controller.
options.InvalidModelStateResponseFactory = ProblemDetailsFactory; | ||
var problemDetails = new ValidationProblemDetails(context.ModelState) | ||
{ | ||
Status = StatusCodes.Status400BadRequest, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be removed, ref. #8688
f7e3eaa
to
8e8f88a
Compare
{ | ||
if (_problemDetailsFactory == null) | ||
{ | ||
_problemDetailsFactory = HttpContext?.RequestServices?.GetRequiredService<ProblemDetailsFactory>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could end up being null
which would lead to a NRE below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's expected to only ever be null in test scenarios. Basically following the same pattern as https://github.com/aspnet/AspNetCore/pull/12298/files#diff-8aa2822b0a420b6f8a04153ca4312519R172
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to check if it's null and throw a better exception then? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@khellang I'd rather keep the code comparable to the other properties for now. I did venture at using nullable types at one point and that forces some cleaning up some of these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not saying you should change this property, I'm saying you might want to guard against null
at the call site. Haven't checked usage of the other properties, but I'd consider those potential bugs as well if not properly guarded against null
.
/// <param name="detail">The value for <see cref="ProblemDetails.Detail" />.</param> | ||
/// <param name="instance">The value for <see cref="ProblemDetails.Instance" />.</param> | ||
/// <returns>The <see cref="ValidationProblemDetails"/> instance.</returns> | ||
public abstract ValidationProblemDetails CreateValidationProblemDetails( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going back to an earlier question about what would you do if you needed to consistently return 422 for ValidationProblem
- this would be the API to override this. This is called by InvalidModelStateFilter
and ControllerBase.ValidationProblem
* Introduce ControllerBase.Problem and ValidationProblem overload that accepts optional parameters * Consistently use ClientErrorData when generating ProblemDetails * Clean-up InvalidModelStateResponseFactory initialization. Fixes #8537
126150c
to
8b1f527
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useful.
that accepts optional parameters
Fixes #8537
Description
Add APIs to return a
ProblemDetails
instance from aControllerBase
. Fixes bugs where instances ofProblemDetails
created in different parts of MVC had different (missing) content.Customer Impact
This is a new API so customers aren't impacted until they use it.
Regression?
No
Risk
Low.