简体   繁体   English

如何在 .NET 核心 5 Web ZDB974238714CA8DE634A7CE1D083A1 项目中自动格式化 API 响应?

[英]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:我有一个带有格式化响应的 API,如下所示 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?我的问题,如何自动将默认响应转换为ApiResponse model 而无需在每个 controller 内的每个操作返回时重复编写new ApiResponse() model?

Hopefully anyone can help me.. Thanks in advance guys希望任何人都可以帮助我..提前谢谢你们

You can use Result Filter in MVC Filter pipeline您可以在 MVC 过滤器管道中使用结果过滤器

MVC Filter pipeline MVC 过滤器管道

So change your controller looks like this所以改变你的 controller 看起来像这样

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

Add new class, called ResultManipulator.cs添加新的 class,名为 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并且不要忘记在 Startup 中添加 ResultManipulator class

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())在 Startup.cs (Configure())

 app.UseYourResponseMiddleware();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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