proudmonkey/AutoWrapper

Inconsistent type for exceptionMessage (AutoWrapper 4.5)

nwoolls opened this issue · 1 comments

Hi there. First of all, thanks for your time and project!

I am having an issue with the latest stable version of the library (4.5) running on .NET Core 6.0.

Specifically, under some circumstances the exceptionMessage JSON property is a string, and in some cases the exceptionMessage JSON property is an object. This poses issues when parsing the result, especially using C# where we expect to be able to map the property to a specific type.

You can reproduce this with the following steps.

From the command line:

dotnet --version
6.0.101
mkdir autowrapper-exceptionmessage-issue
cd autowrapper-exceptionmessage-issue/
dotnet new webapi
dotnet add package AutoWrapper.Core --version 4.5.0

Edit Program.cs and add:

var options = new AutoWrapperOptions
{
    IsDebug = app.Environment.IsDevelopment()
};
app.UseApiResponseAndExceptionWrapper(options);

Edit WeatherForecastController.cs and add:

public class InputModel
{
    [Required(AllowEmptyStrings = false)]
    public string? FirstName { get; set; }
}

[HttpPost]
public IActionResult Post(InputModel inputModel)
{
    if (inputModel.FirstName == "foo")
    {
        return StatusCode(StatusCodes.Status400BadRequest, "An error occurred - returning status code");
    }

    if (inputModel.FirstName == "bar")
    {
        throw new ApiException("An error occurred - throwing ApiException");
    }
    
    return StatusCode(StatusCodes.Status201Created);
}

Run the project.

The following are examples where exceptionMessage may either be an object or a string:

Example 1 - exceptionMessage is an object

curl -X 'POST' \
  'https://localhost:7284/WeatherForecast' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{}'
{
  "isError": true,
  "responseException": {
    "exceptionMessage": {
      "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
      "title": "One or more validation errors occurred.",
      "status": 400,
      "traceId": "00-12ce2cc5118d85bf077d9d4f71342ef0-23dae6d83195f904-00",
      "errors": {
        "FirstName": [
          "The FirstName field is required."
        ]
      }
    }
  }
}

Example 2 - exceptionMessage is an object

curl -X 'POST' \
  'https://localhost:7284/WeatherForecast' \
  -H 'accept: */*' \
  -d '{
  "firstName": "string"
}'
{
  "isError": true,
  "responseException": {
    "exceptionMessage": {
      "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
      "title": "Unsupported Media Type",
      "status": 415,
      "traceId": "00-bc5f37b0710ad9189d09f5de6cfdd981-59a78671fdb1671e-00"
    }
  }
}

Example 3 - exceptionMessage is a string

curl -X 'POST' \
  'https://localhost:7284/WeatherForecast' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "firstName": "foo"
}'
{
  "isError": true,
  "responseException": {
    "exceptionMessage": "An error occurred - returning status code"
  }
}

Example 4 - exceptionMessage is a string

curl -X 'POST' \
  'https://localhost:7284/WeatherForecast' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "firstName": "bar"
}'
{
  "isError": true,
  "responseException": {
    "exceptionMessage": "An error occurred - throwing ApiException"
  }
}

Any guidance on this would be very much appreciated. Thanks in advance!

I've managed to workaround this issue in case of 400 Bad Request by using custom error model together with overriding the ApiBehaviorOptions.InvalidModelStateResponseFactory.

Something like this:

In Program.cs:

_ = builder.Services
    .Configure<ApiBehaviorOptions>(options => options
        .InvalidModelStateResponseFactory = CustomInvalidModelStateResponseFactory.OnActionExecuting);

In CustomInvalidModelStateResponseFactory.cs:

// trimmed for brevity
public static IActionResult OnActionExecuting(ActionContext context)
{
    // trimmed for brevity
    throw new ApiException(new GenericApiError
    {
        error_message = "Request model is invalid.",
        validation_errors = validationErrors,
    });
}