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

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions