简体   繁体   中英

How to autoformat API response in .NET core 5 Web API project?

I Have an API with formatted response look's like this following JSON:

{
    "statusCode": 200,
    "totalRecord": 2,
    "message": "Succesfully get merchants",
    "data": [
        {
            "id": 1,
            "name": "Leo Shop",
            "address": "Flower Street 9A",
            "isComplete": false,
            "createdAt": "2021-05-30T14:16:27.654233",
            "updatedAt": "2021-05-30T14:16:28.515476"
        },
        {
            "id": 3,
            "name": "Test Shop",
            "address": "Playing Street 12A",
            "isComplete": false,
            "createdAt": "2021-05-30T14:16:27.654233",
            "updatedAt": "2021-05-30T14:16:28.515476"
        }
    ]
}

And code behind those response look's like this following code:

[HttpGet]
public async Task<ActionResult<IEnumerable<Merchant>>> GetMerchants()
{
    var data = await _context.Merchants.ToListAsync();
    ApiResponse res = new ApiResponse{StatusCode = 200, Message = "Succesfully get merchants", TotalRecord = data.Count, Data = data}; // Focus on this line
    return Ok(res);
}

My question, how to automatically convert default response As ApiResponse model without repeating to write new ApiResponse() model on every action return inside every controller?

Hopefully anyone can help me.. Thanks in advance guys

You can use Result Filter in MVC Filter pipeline

MVC Filter pipeline

So change your controller looks like this

[HttpGet]
public Task<List<Merchant>> GetMerchants()
{
   return _context.Merchants.ToListAsync();
}

Add new class, called ResultManipulator.cs

  public class ResultManipulator : IResultFilter
        {
            public void OnResultExecuted(ResultExecutedContext context)
            {
                // do nothing
            }
    
            public void OnResultExecuting(ResultExecutingContext context)
            {
                //run code immediately before and after the execution of action results. They run only when the action method has executed successfully. They are useful for logic that must surround view or formatter execution.
                var result = context.Result as ObjectResult;
                var resultObj = result.Value;

    //change this ResultApi with your ApiResponse class
                var resp = new ResultApi
                {
                    Path = context.HttpContext.Request.Path.HasValue ? context.HttpContext.Request.Path.Value : "",
                    Method = context.HttpContext.Request.Method
                };
    
                if (resultObj is not null && resultObj is not Unit)
                    resp.Payload = resultObj;
    
//you can also change this from System.Text.Json to newtonsoft if you use newtonsoft
                context.Result = new JsonResult(resp, new JsonSerializerOptions()
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
                    Converters = { new JsonStringEnumConverter() }
                });
            }
        }

And dont forget to add ResultManipulator class in Startup

services.AddControllers(options =>
            {
                options.Filters.Add(new ResultManipulator());
            })

I hope it solve your problem

Try this as a middleware solution. It will transform the response.

public class YourResponseMiddleware
    {
        private readonly RequestDelegate _next;    
        public YourResponseMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            var existingBody = context.Response.Body;
            using (var newBody = new MemoryStream())
            {
                context.Response.Body = newBody;
                await _next(context);
                var newResponse = await FormatResponse(context.Response);
                context.Response.Body = new MemoryStream();
                newBody.Seek(0, SeekOrigin.Begin);
                context.Response.Body = existingBody;
                var newContent = new StreamReader(newBody).ReadToEnd();
             
                // Send modified content to the response body.
                //
                await context.Response.WriteAsync(newResponse);
            }
        }
    

        private async Task<string> FormatResponse(HttpResponse response)
        {
            //We need to read the response stream from the beginning...and copy it into a string...I'D LIKE TO SEE A BETTER WAY TO DO THIS
            //
            response.Body.Seek(0, SeekOrigin.Begin);
            var content= await new StreamReader(response.Body).ReadToEndAsync();    
            var yourResponse = new YourResponse (); // CREATE THIS CLASS
            yourResponse.StatusCode = response.StatusCode;
            if(!IsResponseValid(response))
            {
                yourResponse.ErrorMessage = content;
            }
            else
            {
                yourResponse.Content = content;
            }
            yourResponse.Size = response.ToString().Length;
            var json = JsonConvert.SerializeObject(yourResponse );

            //We need to reset the reader for the response so that the client an read it
            response.Body.Seek(0, SeekOrigin.Begin);    
            return $"{json}";
        }

        private bool IsResponseValid(HttpResponse response)
        {
            if ((response != null)
                && (response.StatusCode == 200
                || response.StatusCode == 201
                || response.StatusCode == 202))
            {
                return true;
            }
            return false;
        }
    }

 public static class ResponseMiddleware
    {
        public static void UseYourResponseMiddleware(this IApplicationBuilder app)
        {
            app.UseMiddleware<YourResponseMiddleware>();
        }
    }

In Startup.cs (Configure())

 app.UseYourResponseMiddleware();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM