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

Extend Result.Status to include "Created" and Validation Failures #174

Open
nhwilly opened this issue Apr 8, 2024 · 3 comments
Open

Extend Result.Status to include "Created" and Validation Failures #174

nhwilly opened this issue Apr 8, 2024 · 3 comments
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@nhwilly
Copy link

nhwilly commented Apr 8, 2024

Extension methods exist that allow for a derived version of a FastEndpoints endpoint to be used to simply return a Result<T>. It also extends the invalid request behavior by transforming IEnumerable<ValidationError> to a ProblemDetails.

This works well, but falls short in a couple of ways. I believe this could become a go-to approach for endpoints with some small enhancements.

Extend Result.Status enum to include a Created value.

This would allow the derived endpoint to distinguish between a Result.Ok of 200 and a Result.Created of 201.

The challenge: Allowing for the derived endpoints to create a CreatedAt response that includes the route to the new resource. This normally includes a route and at least an id value.

My current endpoint code looks like this:

await SendCreatedAtAsync<GetAccountById>(new 
  { result.Value.Id }, 
  result.Value.ToDto(), 
  cancellation: ct);

So creating Result.Created<T>(T value) would look just exactly like Result.Success<T>.

But there would also have to be a Result.CreatedAt<T>(T? value, object id, string route)

Or something like that. Not sure how FastEndpoints does it or how I would create a generic approach.

Conversion of IEnumerable<ValidationError> to ProblemDetails

NOTE: This is Microsoft.AspNetCore.Mvc.ProblemDetails

This can be done, but FastEndpoints relies on Fluent Validation's ValidationFailure which has more details about the problem. This means that FluentValidation errors that occur in command handlers and queries lose that detail when they are returned upstream using Result.Invalid.

Adding a way to pass up the equivalent of a ValidationFailure using Result.Invalid or some other status code would allow the details to be retained and ultimately properly converted to a ProblemDetails by FastEndpoints (or anything else).

I had done some work on this before I retreated to the standard Ardalis.Result package. (This is from LinqPad). That code is below if it helps anyone.

These are suggestions. I am moving forward with what's working now, and for the record, I did not use a derived class from FastEndpoints, waiting for a more complete solution.

void Main()
{
  var errors = new List<ValidationError>{
  new() {
    Identifier=nameof(Customer),
     ErrorCode="X78",
     ErrorMessage="First error message",
     Severity=ValidationSeverity.Error
  },
  new() {
  Identifier=nameof(Customer),
   ErrorCode="X79",
   ErrorMessage="Second error message",
   Severity=ValidationSeverity.Error
},
new() {
  Identifier=nameof(Order),
   ErrorCode="2301",
   ErrorMessage="Your order could not be processed",
   Severity=ValidationSeverity.Error
},
};
  var result = Result.Invalid(errors.ToArray());
  var problem = result.AsProblemDetails();
}

public record Customer;
public record Order;
public static class ValidationExtensions
{
  public static Dictionary<string, string[]> ToDictionary(this IEnumerable<ValidationError> errors)
  {
    Guard.Against.Null(errors);
    var errorDictionary = errors
      .GroupBy(error => error.Identifier)
      .Select(error => new
      {
        PropertyName = error.Key,
        Errors = error.Select(y => y.ErrorMessage).ToArray()
      })
      .ToDictionary(key => key.PropertyName, value => value.Errors);
    return errorDictionary;
  }
  public static ValidationProblemDetails AsProblemDetails(this IResult result)
  {
    var errors = result.ValidationErrors.ToDictionary();

    var problem = new ValidationProblemDetails(errors)
    {
      Detail = "One or more validation errors occurred.",
      Status = (int)HttpStatusCode.BadRequest,
      Title = "ValidationError",
    };
    return problem;
  }
@nhwilly nhwilly changed the title Extend Result.Status to include "Created" Extend Result.Status to include "Created" and Validation Failures Apr 8, 2024
@ardalis ardalis added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers labels Apr 10, 2024
@sunecko
Copy link
Contributor

sunecko commented May 4, 2024

check this PR

@ardalis
Copy link
Owner

ardalis commented May 16, 2024

@nhwilly is this still an issue or has it been resolved by #177 and other 9.0.x updates?

@nhwilly
Copy link
Author

nhwilly commented May 16, 2024

Traveling overseas. Won't get a hard look at it until end of the month. Thanks to everyone for the work and attention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants