【问题标题】:How to return the same status code from a second API call如何从第二个 API 调用返回相同的状态码
【发布时间】:2020-05-15 14:14:00
【问题描述】:

我有一个 ASP.NET Core API 调用第二个 API。

如果第二个 API 出现错误,我会在我的服务层抛出异常:

var response = await httpClient.SendAsync(request); //call second API

if (!response.IsSuccessStatusCode)
{
    //return HTTP response with StatusCode = X, if response.StatusCode == X
    throw new HttpRequestException(await response.Content.ReadAsStringAsync()); 
    //this always returns 400
}

如何引发异常,该异常将返回第二个 API 调用的状态码相同的响应?

如果我使用HttpRequestException,它将始终返回 400,即使 response 对象具有 StatusCode = 500

编辑: 第一个 API 端点如下所示:

            public async Task<ActionResult<HttpResponseMessage>> CreateTenancy([FromBody]TenancyRequest tenancy)
            {
                //Make some calls...
                return Created(string.Empty, new { TenancyID = newTenancyExternalId });
            }

第二个 API 端点如下所示:

    [HttpPost]
    public IHttpActionResult CreateTenancy([FromBody]TenancyDTO tenancyDTO)
    {    
        var tenancy = GetTenancy();    
        return Created(string.Empty, tenancy);
    }

我尝试过使用throw new HttpResponseException(response);,但这会删除描述性异常消息,有效负载最终如下:

{
    "Code": 500,
    "CorrelationId": "2df08016-e5e3-434a-9136-6824495ed907",
    "DateUtc": "2020-01-30T02:02:48.4428978Z",
    "ErrorMessage": "Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.",
    "ErrorType": "InternalServerError"
}

我想将ErrorMessage 值保留在原始有效负载中:

{
    "Code": 400,
    "CorrelationId": "ff9466b4-8c80-4dab-b5d7-9bba1355a567",
    "DateUtc": "2020-01-30T03:05:13.2397543Z",
    "ErrorMessage": "\"Specified cast is not valid.\"",
    "ErrorType": "BadRequest"
}

最终目标是返回:

{
    "Code": 500,
    "CorrelationId": "ff9466b4-8c80-4dab-b5d7-9bba1355a567",
    "DateUtc": "2020-01-30T03:05:13.2397543Z",
    "ErrorMessage": "\"Specified cast is not valid.\"",
    "ErrorType": "InternalServerError"
}

【问题讨论】:

  • 这行得通吗?抛出新的 HttpRequestException(响应)
  • 不,构造函数不会接受它。
  • 你试过throw new HttpResponseException(response)吗?即响应异常不是请求异常?
  • 创建自定义 Exception 例如ApiException 具有您关心的适当属性,包括 StatusCode 属性,然后是 throw new ApiException(response.StatusCode)。如果你有一个全局异常处理程序/中间件,你可以在那里捕获异常并设置Response.StatusCode
  • @Jawad 请查看我更新的问题

标签: c# http asp.net-core asp.net-core-2.2


【解决方案1】:

您可以简单地进行 API 调用并将其响应代码复制到与 IStatusCodeActionResult 兼容的内容中。

抛出自定义异常的替代方法。创建类似的东西

public class ApiCallException : Exception
{
    public APiCallException(int statusCode, ...)
    {
        ApiStatusCode = statusCode;
    }

    int ApiStatusCode { get; }
    ...
}

并从您的 API 结果中复制状态代码,然后抛出异常。

var response = await httpClient.SendAsync(request); //call second API
if (!response.IsSuccessStatusCode)
{   
    var content = await response.Content.ReadAsStringAsync();
    throw new ApiCallException(500, content); 
}

然后您可以注册一个异常过滤器来处理调用AddMvc时的结果。

services.AddMvc(options => options.Filters.Add<ExceptionFilter>());

ExceptionFilter 可能类似于

public class ExceptionFilter : IExceptionFilter
{
    // ...

    public void OnException(ExceptionContext context)
    {
        if (context.Exception is ApiCallException ace)
        {
            var returnObject = CreateReturnObjectSomehow();
            context.Result = new ObjectResult(returnObject) { StatusCode = ace.StatusCode };
        }
        else
        {
            // do something else
        }
    }
}

【讨论】:

    【解决方案2】:

    我尝试了一些简单的方法,例如更改 API 端点的返回类型并在出现错误时返回对象。否则,构建您自己的 HttpResponseMessage 并返回它。下面的这个 sn-p 使用文本,但如果有的话,您可以使用序列化程序来序列化其他内容。

    public async Task<HttpResponseMessage> Test(string str)
    {
        var httpClient = new HttpClient();
        var request = new HttpRequestMessage(HttpMethod.Get, $"myAPI that returns different errors 400, 404, 500 etc based on str");
    
        var response = await httpClient.SendAsync(request);
        if (!response.IsSuccessStatusCode)
            return response;
    
        // do something else
        return new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent("Your Text here") };
    }
    

    使用过滤器的其他方法

    使用 IHttpActionResult 作为返回类型的另一种方法,您可以使用过滤器将所有 HttpResponseMessages 符合 IHttpActionResult。

    过滤器:创建一个单独的cs文件并使用这个过滤器定义。

    public class CustomObjectResponse : IHttpActionResult
    {
        private readonly object _obj;
    
        public CustomObjectResponse(object obj)
        {
            _obj = obj;
        }
    
        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = _obj as HttpResponseMessage;
            return Task.FromResult(response);
        }
    }
    

    在你的 API 中,你会像这样使用你的过滤器,

    public async Task<IHttpActionResult> Test(string str)
    {
        var httpClient = new HttpClient();
        var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:4500/api/capacity/update-mnemonics/?mnemonic_to_update={str}");
    
        var response = await httpClient.SendAsync(request);
        if (!response.IsSuccessStatusCode)
            return new CustomObjectResponse(response);
    
        // Other Code here
    
        // Return Other objects 
        KeyValuePair<string, string> testClass = new KeyValuePair<string, string>("Sheldon", "Cooper" );
        return new OkWithObjectResult(testClass);
    
        // Or Return Standard HttpResponseMessage
        return Ok();
    
    }
    

    【讨论】:

      【解决方案3】:

      感谢 Jawad 和 Kit 提供了很好的答案,帮助我制定了以下解决方案:

      原来有一些中间件处理异常:

          public async Task Invoke(HttpContext httpContext)
          {
              try
              {
                  await _next(httpContext);
              }
              catch (Exception exception)
              {
                  if (httpContext.Response.HasStarted) throw;
      
                  var statusCode = ConvertExceptionToHttpStatusCode(exception);
      
                  httpContext.Response.Clear();
                  httpContext.Response.StatusCode = (int)statusCode;
                  httpContext.Response.ContentType = "application/json";
      
                  if (statusCode != HttpStatusCode.BadRequest)
                  {
                      _logger.Error(exception, "API Error");
                  }
      
                  await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(new Error(statusCode, httpContext.Request.CorrelationId(), exception.Message, statusCode.ToString())));
              }
          }
      

      Error 类如下所示:

          public class Error
          {
              public int Code { get; }
              public Guid? CorrelationId { get; }
              public DateTime DateUtc { get; }
              public string ErrorMessage { get; }
              public string ErrorType { get; }
      
              public Error(HttpStatusCode code, Guid? correlationId, string errorMessage, string errorType)
              {
                  Code = (int)code;
                  CorrelationId = correlationId;
                  DateUtc = DateTime.UtcNow;
                  ErrorMessage = errorMessage;
                  ErrorType = errorType;
              }
          }
      

      我创建了这个类:

      public class ApiCallException : Exception
      {
          public int StatusCode { get; }
          public override string Message { get; }
          public ApiCallException(int statusCode, string message)
          {
              StatusCode = statusCode;
              Message = message;
          }
      }
      

      然后更新了我的原始代码:

                      if (!response.IsSuccessStatusCode)
                      {
                          throw new ApiCallException((int)response.StatusCode, await response.Content.ReadAsStringAsync());
                      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-11
        • 2020-12-04
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多