Implementation of ASP. NET Core AutoWrapper Custom Response Output
- 2021-11-14 05:20:30
- OfStack
Preface
AutoWrapper is a simple customizable global exception handler and wrapper for the ASP. NET Core API response. He uses ASP. NET, Core, middleware to intercept incoming HTTP requests, and wraps the final result automatically in the Uniform 1 format. The main purpose is to focus more on business-specific code requirements and to allow the wrapper to automatically process HTTP responses. This speeds up development time when building the API and tries our 1 standard for the HTTP response.
Installation
AutoWrapper. Core Download and install from NuGet or through CLI
PM> Install-Package AutoWrapper.Core
Register the following in the Startup. cs Configure method, but remember to put it before UseRouting
app.UseApiResponseAndExceptionWrapper();
Start attribute mapping
By default, AutoWrapper will output the following format when a successful request succeeds:
{
"message": "Request successful.",
"isError": false,
"result": [
{
"id": 7002,
"firstName": "Vianne",
"lastName": "Durano",
"dateOfBirth": "2018-11-01T00:00:00"
}
]
}
If we don't like the default attribute naming, we can map it to any name we need to specify through the AutoWrapperPropertyMap attribute. For example, we can change the name of the result attribute to data. As shown below
public class MapResponseObject
{
[AutoWrapperPropertyMap(Prop.Result)]
public object Data { get; set; }
}
Then pass the MapResponseObject class to AutpWrapper middleware
app.UseApiResponseAndExceptionWrapper<MapResponseObject>();
After re-requesting through mapping, the impact format is now as follows
{
"message": "Request successful.",
"isError": false,
"data": {
"id": 7002,
"firstName": "Vianne",
"lastName": "Durano",
"dateOfBirth": "2018-11-01T00:00:00"
}
}
You can see that the result attribute has been replaced with the data attribute
By default, AutoWrapper will spit out the following response format when an exception occurs
{
"isError": true,
"responseException": {
"exceptionMessage": "Unhandled Exception occurred. Unable to process the request."
}
}
And if IsDebug is set in AutoWrapperOptions, similar information with stack trace information will be generated
{
"isError": true,
"responseException": {
"exceptionMessage": " Input string was not in a correct format.",
"details": " at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)\r\n at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)\r\n … "
}
}
If you want to change some APIError attribute names to other names, just add the following mapping MapResponseObject in the following code
public class MapResponseObject
{
[AutoWrapperPropertyMap(Prop.ResponseException)]
public object Error { get; set; }
[AutoWrapperPropertyMap(Prop.ResponseException_ExceptionMessage)]
public string Message { get; set; }
[AutoWrapperPropertyMap(Prop.ResponseException_Details)]
public string StackTrace { get; set; }
}
Simulate the error with the following code
int num = Convert.ToInt32("10s");
Now the mapped output is as follows
app.UseApiResponseAndExceptionWrapper();
0
Note that APIError now changes the default properties of the model based on the properties defined in the MapResponseObject class.
We are free to choose to map any attribute. The following is a list of mapping attributes
app.UseApiResponseAndExceptionWrapper();
1
Custom error schema
AutoWrapper also provides an object that APIException can use to define its own exception. If you want to throw your own exception message, you can simply do the following
app.UseApiResponseAndExceptionWrapper();
2
The default output format is as follows
{
"isError": true,
"responseException": {
"exceptionMessage": "Error blah",
"referenceErrorCode": "511",
"referenceDocumentLink": "http://blah.com/error/511"
}
}
Of course, we can customize the error format
app.UseApiResponseAndExceptionWrapper();
4
Then we can raise our error with the following code
app.UseApiResponseAndExceptionWrapper();
5
The output format is as follows
app.UseApiResponseAndExceptionWrapper();
6
Use a custom API response format
If the mapping doesn't meet our needs. And we need to add other attributes to the API response model, we can now customize our own format class by setting UseCustomSchema to true, as follows
app.UseApiResponseAndExceptionWrapper();
7
Now assuming that we want to include 1 attribute SentDate and Pagination objects in the response in the main API, we might want to define the API response model to the following format
public class MyCustomApiResponse
{
public int Code { get; set; }
public string Message { get; set; }
public object Payload { get; set; }
public DateTime SentDate { get; set; }
public Pagination Pagination { get; set; }
public MyCustomApiResponse(DateTime sentDate, object payload = null, string message = "", int statusCode = 200, Pagination pagination = null)
{
this.Code = statusCode;
this.Message = message == string.Empty ? "Success" : message;
this.Payload = payload;
this.SentDate = sentDate;
this.Pagination = pagination;
}
public MyCustomApiResponse(DateTime sentDate, object payload = null, Pagination pagination = null)
{
this.Code = 200;
this.Message = "Success";
this.Payload = payload;
this.SentDate = sentDate;
this.Pagination = pagination;
}
public MyCustomApiResponse(object payload)
{
this.Code = 200;
this.Payload = payload;
}
}
public class Pagination
{
public int TotalItemsCount { get; set; }
public int PageSize { get; set; }
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
}
Test the results with the following code fragment
app.UseApiResponseAndExceptionWrapper();
9
After running, you will get the following influence format
{
"code": 200,
"message": "Success",
"payload": [
{
"id": 1,
"firstName": "Vianne",
"lastName": "Durano",
"dateOfBirth": "2018-11-01T00:00:00"
},
{
"id": 2,
"firstName": "Vynn",
"lastName": "Durano",
"dateOfBirth": "2018-11-01T00:00:00"
},
{
"id": 3,
"firstName": "Mitch",
"lastName": "Durano",
"dateOfBirth": "2018-11-01T00:00:00"
}
],
"sentDate": "2019-10-17T02:26:32.5242353Z",
"pagination": {
"totalItemsCount": 200,
"pageSize": 10,
"currentPage": 1,
"totalPages": 20
}
}
But note from here that once we customize the API response, it means that we have complete control over how the data is formatted, and we have lost some option configuration of the default API response. However, we can still use the ApiException () method to raise user-defined error messages
As shown below
[Route("{id:long}")]
[HttpPut]
public async Task<MyCustomApiResponse> Put(long id, [FromBody] PersonDTO dto)
{
if (ModelState.IsValid)
{
try
{
var person = _mapper.Map<Person>(dto);
person.ID = id;
if (await _personManager.UpdateAsync(person))
return new MyCustomApiResponse(DateTime.UtcNow, true, "Update successful.");
else
throw new ApiException($"Record with id: {id} does not exist.", 400);
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, ex, "Error when trying to update with ID:{@ID}", id);
throw;
}
}
else
throw new ApiException(ModelState.AllErrors());
}
You can now get the default response format when you validate the model
{
"isError": true,
"responseException": {
"exceptionMessage": "Request responded with validation error(s). Please correct the specified validation errors and try again.",
"validationErrors": [
{
"field": "FirstName",
"message": "'First Name' must not be empty."
}
]
}
}
Reference
https://github.com/proudmonkey/AutoWrapper