簡體   English   中英

如何從第二個 API 調用返回相同的狀態代碼

[英]How to return the same status code from a second API call

我有一個 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"
}

我嘗試了一些簡單的方法,例如更改 API 端點的返回類型並在出現錯誤時按原樣返回對象。 否則,構建您自己的 HttpResponseMessage 並返回它。 下面的這個片段使用文本,但如果你有的話,你可以使用序列化器來序列化其他內容。

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();

}

您可以簡單地進行 API 調用並將其響應代碼復制到與IStatusCodeActionResult兼容的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
        }
    }
}

感謝 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());
                }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM