-
Notifications
You must be signed in to change notification settings - Fork 110
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
Allowing for validation and other errors to be returned as standard ProblemDetails
#193
Comments
I believe that creating a bespoke, AR Same deserialization could occur. Or does this create an implied dependency? |
If we want to standardize the response, then we need to override the FE validation response. There's a hook to do that. FYI |
Legit question:
I can use JSON attributes to configure the response to take the shape most people are used to seeing while the deserialization still works. Is my custom problem details better off supporting two different types of collections, or just one where many values are nullable depending on the type of content? |
Update: Creating a custom ProblemDetails classAs a first pass, I've written an extension method to use in place of This approach requires a replacement for MVC's This class allows for This approach creates two problems:
private static Microsoft.AspNetCore.Http.IResult UnavailableEntity(IResult result)
{
var problem = new ProblemDetails
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4",
Title = "Critical errors occurred",
Status = (int)HttpStatusCode.ServiceUnavailable,
Errors = result.Errors.Any() ? result.Errors : null,
ValidationErrors = result.ValidationErrors.Any() ? result.ValidationErrors.ToDto() : null
};
return Results.Json(problem, _jsonOptions, ProblemContentType, 500);
} Using MVC's ProblemDetail ExtensionsI like this idea best as it keeps things close to the framework. However, the validation errors and errors will end up in Extensions = new Dictionary<string, object?> { { "errors", validationErrors } } This Everything serializes just fine. Clients, however can't deserialize It is possible to resolve this with a Examples in the wild show people checking the Current Package EnhancementIn the current version of the In Any Case
Considerations for using MVC ProblemDetails
Working on this today... Looking for input |
Success - 2xx {
"type": "MyValidationErrorType",
"title": null,
"status": 400,
"detail": null,
"instance": null,
"errors": {
"severity": "Info"
}
}
{
"type": "MyValidationErrorType",
"title": null,
"status": 400,
"detail": null,
"instance": null,
"errors": [
{
"identifier": "PropertyName",
"errorMessage": "you idiot!",
"errorCode": "myCode",
"severity": 2
}
]
}
{
"type": "MyValidationErrorType",
"title": null,
"status": 400,
"detail": null,
"instance": null,
"validationErrors": [
{
"identifier": "PropertyName",
"errorMessage": "you idiot!",
"errorCode": "myCode",
"severity": 2
}
],
"errors": {
"severity": "Info"
}
} |
OK, I've written something that works for me. But during our dB meeting you spoke about swapping some statements around to avoid casting as Here's the awful workaround I ended up with, but I don't know how you intended it to work. When I try to cast Am I using this public static Microsoft.AspNetCore.Http.IResult ToApiResult<T>(this Result<T> result)
{
return result.Status switch
{
ResultStatus.Ok => result is Result ? Results.Ok() : Results.Ok(result.GetValue()),
ResultStatus.Created => Results.Created(result.Location, result.GetValue()),
ResultStatus.NotFound => NotFoundEntity(result),
ResultStatus.Unauthorized => Results.Unauthorized(),
ResultStatus.Forbidden => Results.Forbid(),
ResultStatus.Invalid => BadEntity(result),
ResultStatus.Error => UnprocessableEntity(result),
ResultStatus.Conflict => ConflictEntity(result),
ResultStatus.Unavailable => UnavailableEntity(result),
ResultStatus.CriticalError => CriticalEntity(result),
_ => throw new NotSupportedException($"Result {result.Status} conversion is not supported."),
};
}
internal static Microsoft.AspNetCore.Http.IResult ToApiResult(this Result result)
{
return result.Status switch
{
ResultStatus.Ok => result is Result ? Results.Ok() : Results.Ok(result.GetValue()),
ResultStatus.Created => Results.Created(result.Location, result.GetValue()),
ResultStatus.NotFound => NotFoundEntity(result),
ResultStatus.Unauthorized => Results.Unauthorized(),
ResultStatus.Forbidden => Results.Forbid(),
ResultStatus.Invalid => BadEntity(result),
ResultStatus.Error => UnprocessableEntity(result),
ResultStatus.Conflict => ConflictEntity(result),
ResultStatus.Unavailable => UnavailableEntity(result),
ResultStatus.CriticalError => CriticalEntity(result),
_ => throw new NotSupportedException($"Result {result.Status} conversion is not supported."),
};
} |
I don't see where you are trying to cast |
The result of casting |
Hi all, I don't think this library should handle all existing As a first example how I did it:
Now all my ajax calls to MVC endpoints return a standard response, with error codes that enable localized messages to the user from frontend. |
Definitely could (always) use more docs... We do have them at: https://result.ardalis.com/ |
The collision of
FastEndpoints
(FE),FluentValidation
(FV),Asp.Net Core
(MVC) andArdalis.Result
(AR) in the same application begs for a standardized error response.The Validation Issue
Error
class so that FVValidationFailure
details are not lost.Invalid
result containing aValidationError
. When using AR in the domain model, an ARValidationError
can be used or a FVValidationFailure
can be converted to an ARValidationError
using theAsErrors()
extension method.ToMinimalApiResult()
is called on theresult
, the client receives a 400 status code but with a completely different payload than the 400 generated by the FE pipeline validation.Other than catching deserializing exceptions on the client there's no way to know which invalid response shape was received.
The Error Detail Issue
ErrorList
object which contains aCorrelationId
and a collection of strings. TheToMinimalApiResult()
method simply takes the collection of strings and concatenates them into theDetail
property. TheCorrelationId
is lost.Possible Enhancements
ProblemDetails
class in the same fashion as FE did. This seems to be the most popular approach. In this way an AR problem details class could contain a collection of validation errors and a collection of error strings so that all error laden responses could be deserialized to a single type. With proper JSON attributes null collections could be ignored.HttpValidationProblemDetails
only supports collections of strings as errors and so additionalValidationError
properties will be lost.Extensions
property ofProblemDetails
. This works well and avoids new types, but the consumer will still need to know if there are simple errors or validation errors contained the JSON. Further, deserializing requires special treatment per this comment in the library -As expected, a quick attempt to deserialize did not fully recreate the problem details properly.
Looking for guidance on which way to proceed.
The text was updated successfully, but these errors were encountered: