Skip to content

Use/Inject ProblemDetailsFactory for building ProblemDetails instances #36

Description

@alexmurari

Hey @DerStimmler,

First of all, thank you for this project!

Currently, I map Results like this:

// Some ResultExtensions.cs class...

public static ActionResult<T> ToActionResult<T>(this Result<T, Error> result, ControllerBase controller, Func<T, ActionResult>? onSuccess = null)
{
    if (result.IsFailure)
        return result.Error.ToProblem(controller);

    return onSuccess is null
        ? controller.Ok(result.Value)
        : onSuccess(result.Value);
}

private static ObjectResult ToProblem(this Error error, ControllerBase controller)
{
    var statusCode = GetStatusCode(error);
    var useValidationFormat = IsValidation(error);

    if (useValidationFormat)
        return CreateValidationProblem(controller, error, statusCode);

    return CreateStandardProblem(controller, error, statusCode);
}

private static ObjectResult CreateValidationProblem(ControllerBase controller, Error error, int statusCode)
{
    var problem = controller.ProblemDetailsFactory.CreateValidationProblemDetails(
        controller.HttpContext,
        new ModelStateDictionary(),
        statusCode);

    problem.Extensions["errors"] = new[] { error };

    return new ObjectResult(problem) { StatusCode = statusCode };
}

private static ObjectResult CreateStandardProblem(ControllerBase controller, Error error, int statusCode)
{
    var problem = controller.ProblemDetailsFactory.CreateProblemDetails(
        controller.HttpContext,
        statusCode,
        detail: error.Message);

    problem.Extensions["errors"] = new[] { error };

    return new ObjectResult(problem) { StatusCode = statusCode };
}

// ...

And in the controller:

[HttpPost("SignUp")]
public async Task<ActionResult<AccountSignUpResponseDto>> SignUpAsync(AccountSignUpRequestDto requestDto, CancellationToken cancellationToken)
{
    var response = await mediator.Send(new SignUpCommand(requestDto), cancellationToken);
    return response.ToActionResult(this);
}

This approach is very clean and works great, but I would love to use your library—which is very elegant—and stop maintaining my own ResultExtensions class.

The issue: I heavily rely on a custom ProblemDetailsFactory (notice that I method-inject the controller instance to obtain it) that is responsible for generating all of my ProblemDetails instances. It is used in other areas of my application—such as the rate-limiter middleware, authentication/authorization, etc.—to ensure my app generates consistent problem details across all endpoints.

Is there a way to inject or use a custom ProblemDetailsFactory within your library? If so, I would love to start using it!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions