如何 return 来自第二个 API 调用的相同状态代码
How to return the same status code from a second API call
我有一个 ASP.NET 核心 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
}
我如何抛出一个异常,该异常将 return 具有来自第二个 API 调用的相同状态代码的响应?
如果我使用 HttpRequestException
它总是 return 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"
}
最终目标是 returned:
{
"Code": 500,
"CorrelationId": "ff9466b4-8c80-4dab-b5d7-9bba1355a567",
"DateUtc": "2020-01-30T03:05:13.2397543Z",
"ErrorMessage": "\"Specified cast is not valid.\"",
"ErrorType": "InternalServerError"
}
您可以简单地进行 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
}
}
}
我尝试了一些简单的事情,例如更改 API 端点的 return 类型,并在出现错误时 return 将对象按原样 return。否则,构建您自己的 HttpResponseMessage 和 return。下面的代码片段使用了文本,但您可以使用序列化器序列化其他内容(如果有的话)。
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") };
}
Other approach of using Filters
使用 IHttpActionResult 作为您的 return 类型的另一种方法,您可以使用过滤器使所有 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();
}
感谢 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
class 看起来像这样:
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;
}
}
我创建了这个 class:
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());
}
我有一个 ASP.NET 核心 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
}
我如何抛出一个异常,该异常将 return 具有来自第二个 API 调用的相同状态代码的响应?
如果我使用 HttpRequestException
它总是 return 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"
}
最终目标是 returned:
{
"Code": 500,
"CorrelationId": "ff9466b4-8c80-4dab-b5d7-9bba1355a567",
"DateUtc": "2020-01-30T03:05:13.2397543Z",
"ErrorMessage": "\"Specified cast is not valid.\"",
"ErrorType": "InternalServerError"
}
您可以简单地进行 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
}
}
}
我尝试了一些简单的事情,例如更改 API 端点的 return 类型,并在出现错误时 return 将对象按原样 return。否则,构建您自己的 HttpResponseMessage 和 return。下面的代码片段使用了文本,但您可以使用序列化器序列化其他内容(如果有的话)。
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") };
}
Other approach of using Filters
使用 IHttpActionResult 作为您的 return 类型的另一种方法,您可以使用过滤器使所有 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();
}
感谢 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
class 看起来像这样:
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;
}
}
我创建了这个 class:
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());
}