AutoWrapper
A simple, yet customizable global exception handler and Http response wrapper for ASP.NET Core APIs.
Install / Use
/learn @proudmonkey/AutoWrapperREADME
AutoWrapper

Language: English | 中文
AutoWrapper is a simple, yet customizable global HTTP exception handler and response wrapper for ASP.NET Core APIs. It uses an ASP.NET Core middleware to intercept incoming HTTP requests and automatically wraps the responses for you by providing a consistent response format for both successful and error results. The goal is to let you focus on your business code specific requirements and let the wrapper automatically handle the HTTP response. This can speedup the development time when building your APIs while enforcing own standards for your HTTP responses.
Main features:
- Exception handling.
ModelStatevalidation error handling (support bothData AnnotationandFluentValidation).- A configurable
APIexception. - A consistent response format for
ResultandErrors. - A detailed
Resultresponse. - A detailed
Errorresponse. - A configurable
HTTPStatusCodesand messages. - Add support for
Swagger. - Add Logging support for
Request,ResponseandExceptions. - A configurable middleware
optionsto configure the wrapper. See Options section below for details. - Enable property name mappings for the default
ApiResponseproperties. - Add support for implementing your own user-defined
ResponseandErrorschema / object. - Add support for Problem Details exception format.
- Add support for ignoring action methods that don't need to be wrapped using
[AutoWrapIgnore]filter attribute. - V3.x enable backwards compatibility support for
netcoreapp2.1andnetcoreapp2.2.NET Core frameworks. - Add
ExcludePathsoption to enable support forSignalRanddaprroutes。
Installation
- Download and Install the latest
AutoWrapper.Corefrom NuGet or via CLI:
PM> Install-Package AutoWrapper.Core -Version 4.5.1
- Declare the following namespace within
Startup.cs
using AutoWrapper;
- Register the
middlewarebelow within theConfigure()method ofStartup.cs"before" theUseRouting()middleware:
app.UseApiResponseAndExceptionWrapper();
That's simple! Here’s how the response is going to look like for the default ASP.NET Core API template “WeatherForecastController” API:
{
"message": "GET Request successful.",
"result": [
{
"date": "2019-09-16T23:37:51.5544349-05:00",
"temperatureC": 21,
"temperatureF": 69,
"summary": "Mild"
},
{
"date": "2019-09-17T23:37:51.554466-05:00",
"temperatureC": 28,
"temperatureF": 82,
"summary": "Cool"
},
{
"date": "2019-09-18T23:37:51.554467-05:00",
"temperatureC": 21,
"temperatureF": 69,
"summary": "Sweltering"
},
{
"date": "2019-09-19T23:37:51.5544676-05:00",
"temperatureC": 53,
"temperatureF": 127,
"summary": "Chilly"
},
{
"date": "2019-09-20T23:37:51.5544681-05:00",
"temperatureC": 22,
"temperatureF": 71,
"summary": "Bracing"
}
]
}
Defining Your Own Custom Message
To display a custom message in your response, use the ApiResponse object from AutoWrapper.Wrappers namespace. For example, if you want to display a message when a successful POST has been made, then you can do something like this:
[HttpPost]
public async Task<ApiResponse> Post([FromBody]CreatePersonRequest createRequest)
{
try
{
var personId = await _personManager.CreateAsync(createRequest);
return new ApiResponse("New record has been created in the database.", personId, Status201Created);
}
catch (Exception ex)
{
//TO DO: Log ex
throw;
}
}
Running the code will give you the following result when successful:
{
"message": "New record has been created in the database.",
"result": 100
}
The ApiResponse object has the following overload constructors that you can use:
ApiResponse(string message, object result = null, int statusCode = 200, string apiVersion = "1.0.0.0")
ApiResponse(object result, int statusCode = 200)
ApiResponse(int statusCode, object apiError)
ApiResponse()
Defining Your Own Api Exception
AutoWrapper provides two flavors that you can use to define your own custom exception:
ApiException- defaultApiProblemDetailsException- available only in version 4 and up.
Here are a few examples for throwing your own exception message.
Capturing ModelState Validation Errors
if (!ModelState.IsValid)
{
throw new ApiException(ModelState.AllErrors());
}
The format of the exception result would look something like this when validation fails:
{
"isError": true,
"responseException": {
"exceptionMessage": "Request responded with one or more validation errors occurred.",
"validationErrors": [
{
"name": "LastName",
"reason": "'Last Name' must not be empty."
},
{
"name": "FirstName",
"reason": "'First Name' must not be empty."
},
{
"name": "DateOfBirth",
"reason": "'Date Of Birth' must not be empty."
}
]
}
}
To use Problem Details as an error format, just set UseApiProblemDetailsException to true:
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { UseApiProblemDetailsException = true });
Then you can use the ApiProblemDetailsException object like in the following:
if (!ModelState.IsValid)
{
throw new ApiProblemDetailsException(ModelState);
}
The format of the exception result would now look something like this when validation fails:
{
"isError": true,
"type": "https://httpstatuses.com/422",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Your request parameters didn't validate.",
"instance": null,
"extensions": {},
"validationErrors": [
{
"name": "LastName",
"reason": "'Last Name' must not be empty."
},
{
"name": "FirstName",
"reason": "'First Name' must not be empty."
},
{
"name": "DateOfBirth",
"reason": "'Date Of Birth' must not be empty."
}
]
}
You can see how the validationErrors property is automatically populated with the violated name from your model.
Throwing Your Own Exception Message
An example using ApiException:
throw new ApiException($"Record with id: {id} does not exist.", Status404NotFound);
The result would look something like this:
{
"isError": true,
"responseException": {
"exceptionMessage": "Record with id: 1001 does not exist.",
}
}
An example using ApiProblemDetailsException:
throw new ApiProblemDetailsException($"Record with id: {id} does not exist.", Status404NotFound);
The result would look something like this:
{
"isError": true,
"type": "https://httpstatuses.com/404",
"title": "Record with id: 1001 does not exist.",
"status": 404,
"detail": null,
"instance": null,
"extensions": {}
}
The ApiException object contains the following overload constructors that you can use to define an exception:
ApiException(string message, int statusCode = 400, string errorCode = "", string refLink = "")
ApiException(IEnumerable<ValidationError> errors, int statusCode = 400)
ApiException(System.Exception ex, int statusCode = 500)
ApiException(object custom, int statusCode = 400)
The ApiProblemDetailsException object contains the following overload constructors that you can use to define an exception:
ApiProblemDetailsException(int statusCode)
ApiProblemDetailsException(string title, int statusCode)
ApiProblemDetailsException(ProblemDetails details)
ApiProblemDetailsException(ModelStateDictionary modelState, int statusCode = Status422UnprocessableEntity)
For more information, checkout the links below at the Samples section.
Implement Model Validations
Model validations allows you to enforce pre-defined validation rules at a class/property level. You'd normally use this validation technique to keep a clear separation of concerns, so your validation code becomes much simpler to write, maintain, and test.
As you have already known, starting ASP.NET Core 2.1, it introduced the ApiController attribute which performs automatic model state validation for 400 Bad Request error. When the Controller is decorated with ApiController attribute, the framework will automatically register a ModelStateInvalidFilter which runs on the OnActionExecuting event. This checks for the Model State validity and returns the response accordingly. This is a great feature, but since we want to return a custom response object instead of the 400 Bad Request error, we will disable this feature in our case.
To disable the automatic model state validation, just add the following code at ConfigureServices() method in Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}
Enable Property Mappings
Note: Prop
