返回所有使用.NetCore中间件的控制器方法的通用响应对象/json

qpgpyjmq  于 2023-08-08  发布在  .NET
关注(0)|答案(3)|浏览(135)

我的控制器:

public class MyController : ControllerBase
{
  [HttpGet("/getResponse/{userId}")]
  public async Task<MyResponse> GetResponseAsync(long userId)
  {
     return await _myService.GetResponseAsync(userId);
  }
  [HttpGet("/getUser/{userId}")]
  public async Task<MyUser> GetUserAsync(long userId)
  {
     return await _myService.GetUserAsync(userId);
  }
}

字符串
虽然我的控制器返回一个MyResponse或MyUser类的对象,但我希望返回一个自定义的通用响应对象,即

public class ApiResponse
{
  public object Response { get; set; }
  public bool IsSuccess { get; set; }
  public IList<string> Errors { get; set; }
  public ApiResponse(object response, bool success, IList<string> errors = null)
  {
     this.Response = response;
     this.IsSuccess = success;
     this.Errors = errors;
  }
    
  public ApiResponse()
  {
  }
}


我尝试使用IActionFilter的OnActionExecuted(ActionExecutedContext context)来读取响应对象,但在调试变量a时,我没有在主体中看到MyUser或MyResponse对象的任何响应。

public void OnActionExecuted(ActionExecutedContext context)
        {
            ApiResponse _response = new ApiResponse();
            var httpResponse = context.HttpContext.Response;
            if (httpResponse != null)
            {
                if (httpResponse.StatusCode == 200)
                {
                   var a = httpResponse.Body;
                }
            }
        }


有没有一种方法可以读取对象的类型,并将该特定对象分配给ApiResponse.response属性?

weylhg0b

weylhg0b1#

有几种类型的MVC过滤器,IActionFilter不是完成这项工作的最佳工具。你需要的是ResultFilter。
请查看官方文档https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0了解更多详细信息。
这里提供的答案可能对您也有帮助:https://stackoverflow.com/a/64725441/2895299
然而,它也将与您的过滤器实现一起工作,您只是在错误的地方寻找响应对象。您的对象将可在以下位置访问:

context.Result

字符串

  • HttpContext* 对象中的响应将在以后访问,而不是在MVC管道的过滤器阶段。请注意,context.HttpContext.Response.HasStarted 等于false。

更重要的是,HttpContext 中可用的响应/请求不会被类型化(绑定到模型),例如,它将是JSON格式,如 {“userId”:“123”,“用户名”:“Foo”}

zzwlnbp8

zzwlnbp82#

已经有IETF RFC 7807用于HTTP API问题响应,具有自己的内容类型application/problem+json。NETCore使用它返回自动模型验证失败响应。
当客户端和库实际上使用一个标准时,使用自定义格式是一个坏主意。
示例响应可以是:

HTTP/1.1 403 Forbidden
   Content-Type: application/problem+json
   Content-Language: en

   {
    "type": "https://example.com/probs/out-of-credit",
    "title": "You do not have enough credit.",
    "detail": "Your current balance is 30, but that costs 50.",
    "instance": "/account/12345/msgs/abc",
    "balance": 30,
    "accounts": ["/account/12345",
                 "/account/67890"]
   }

字符串
或者是

HTTP/1.1 400 Bad Request
   Content-Type: application/problem+json
   Content-Language: en

   {
   "type": "https://example.net/validation-error",
   "title": "Your request parameters didn't validate.",
   "invalid-params": [ {
                         "name": "age",
                         "reason": "must be a positive integer"
                       },
                       {
                         "name": "color",
                         "reason": "must be 'green', 'red' or 'blue'"}
                     ]
   }


ASP.NET Core使用ProblemDetails和更具体的ValidationProblemDetails类来报告错误。
可以使用ProblemValidationProblem函数发送Problem或ValidationProblem响应,例如:

var details=new ValidationProblemDetails(someDictionary);
return ValidationProblem(details);


Handle errors in ASP.NET Core web APIs文章展示了如何配置ASP.NET Core以返回特定异常的自定义ProblemDetails

cfh9epnr

cfh9epnr3#

我们强烈建议端点提供一致的响应,因为它可以为消费者提供重要的帮助。
响应有两种状态

  • OnSuccess
  • OnError

我们需要对这两种状态进行标准化,以便消费者可以轻松地预测来自端点的响应。它还有助于在修改现有代码时减少破坏性更改。
对于asp.net,核心中间件是为现有应用程序实现通用响应的最佳选择。下面是实施解决方案的分步指南。
1-创建通用响应DTO

public class ResponseModel<T>
{
    public bool Success { get; set; }
    public T Data { get; set; }
    public string ErrorMessage { get; set; }
}

字符串
2-创建中间件处理OnSuccess状态。

public class ResponseMiddleware
{
    private readonly RequestDelegate _next;

    public ResponseMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var originalBodyStream = context.Response.Body;

        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;

            await _next(context);

            context.Response.Body.Seek(0, SeekOrigin.Begin);
            var responseBodyContent = await new StreamReader(context.Response.Body).ReadToEndAsync();
            context.Response.Body.Seek(0, SeekOrigin.Begin);

            if (context.Response.StatusCode == 200 && !context.Response.HasStarted)
            {
                // Deserialize the original response content
                var originalResponse = JsonConvert.DeserializeObject(responseBodyContent);

                // Wrap the response in ResponseModel<T>
                var genericResponse = new ResponseModel<object>
                {
                    Success = true,
                    Data = originalResponse,
                    ErrorMessage = null // No error message if successful
                };

                // Serialize the ResponseModel<T> to the response body
                var jsonResponse = JsonConvert.SerializeObject(genericResponse);
                await context.Response.WriteAsync(jsonResponse);
            }
            else
            {
                // If the response status code is not 200, return the original response content
                await responseBody.CopyToAsync(originalBodyStream);
            }
        }
    }
}


3-在statup.cs中注册中间件

public class Startup
{
    // ...

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...

        // Add the ResponseMiddleware to the request pipeline
        app.UseMiddleware<ResponseMiddleware>();

        // ...
    }
}


4-处理异常OnError状态

public class ResponseExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        context.ExceptionHandled = true;

        // Wrap the exception in ResponseModel<T>
        var genericResponse = new ResponseModel<object>
        {
            Success = false,
            Data = null, // No data if an exception occurred
            ErrorMessage = context.Exception.Message // Set the error message from the exception
        };

        context.Result = new ObjectResult(genericResponse)
        {
            StatusCode = 500 // You can customize the error status code
        };
    }
}

public class Startup
{
    // ...

    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        services.AddControllers(options =>
        {
            // Add the ResponseExceptionFilter as a global exception filter
            options.Filters.Add(typeof(ResponseExceptionFilter));
        });

        // ...
    }
}

相关问题