在 Web API 调用导致异常后处理响应中内容的最佳实践
Best practice to handle content in the response after a Web API call results in an exception
我正在开发 Core 3.1 Web API 和使用它的 MVC 应用程序。在 MVC 应用程序中,我 UserRepo
设置了包含向 API:
发送请求的方法
public class UserRepo : IUserRepo
{
private readonly IHttpClientFactory _clientFactory;
public UserRepo(IHttpClientFactory httpClientFactory)
{
_clientFactory = httpClientFactory;
}
public async Task<User> GetById(int Id)
{
// same code structure as Update ...
}
public async Task<User> Update(User user)
{
HttpClient client = _clientFactory.CreateClient("NamedClient");
try
{
HttpResponseMessage response = await client.PutAsync($"api/Users/{user.Id}", ContentEncoder.Encode(user));
return await response.Content.ReadFromJsonAsync<User>();
}
catch (Exception ex)
{
throw;
}
}
public async Task<User> Insert(User user)
{
// same code structure as Update ...
}
}
Update
方法从不抛出从 API 返回的 400、404 等错误,从而导致静默错误。我发现要引起异常,我需要调用 response.EnsureSuccessStatusCode();
,它起作用了。
但是,异常不包含我需要找出 API 调用出了什么问题的内容。如果发生 400 错误,将抛出一个异常,说明发生了 400 错误,但不会 why 发生。 why 返回到 response
变量,由于我已经实施了验证,它可能看起来像这样:
{
"errors": {
"FirstName": [
"The FirstName field is required."
]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|502d647b-4c7425oa321c8c7b."
}
是否有一种广泛使用的方法来处理在 API 中产生错误后返回的响应?我想知道为什么会出现 400 错误,以便我知道要修复什么。我只是不知道处理这些响应消息的“正确”方式是什么。
我的一个想法是捕获异常并在每次抛出异常之前将其与响应文本一起记录下来。然后,当我的应用程序崩溃时,我可以转到日志并阅读返回的消息。 Update
方法如下所示:
public async Task<User> Update(User user)
{
HttpClient client = _clientFactory.CreateClient("NamedClient");
HttpResponseMessage response = await client.PutAsync($"api/Users/{user.Id}", ContentEncoder.Encode(user));
try
{
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
string errorMessage = await response.Content.ReadAsStringAsync()
_logger.LogError(ex, errorMessage);
throw;
}
return await response.Content.ReadFromJsonAsync<User>();
}
另一个想法是也许可以将消息添加到异常本身并在抛出时看到它?将消息添加为内部异常有意义吗?
Is there a widely used way to handle the response that comes back after an error is produced in the API? I want to know why a 400 error occurred so I know what to fix. I just don't know what is the "right" way to handle these response messages.
通常,异常详细信息只会被记录,而不会 returned。这是因为详细信息可能包括个人身份信息或可能揭示潜在安全漏洞的技术细节。有一个 error details RFC 变得越来越普遍,但即使这样也不应该包含 PII 或堆栈跟踪等详细信息。
在一个 API(MVC 端点)调用另一个 API(实际的 API)的情况下,MVC 端点应该 return 5xx 范围。此处可以接受 500 或 502。所有此类错误及其详细信息都应记录在服务器端。
请注意,如果传播异常,默认行为是 return 500,因此您真正需要做的就是保持 throw;
。但是,在“管道”中执行错误 logging 是正常的,例如 ASP.NET Core 的中间件或 ASP.NET MVC 的全局安装的动作过滤器之类的东西.这是为了确保记录所有错误,同时避免重复。
EnsureSuccessStatusCode
如果 StatusCode
与 2xx 不同,则抛出 HttpRequestException
。
为了从响应中获取最多的信息,您必须手动检索它。
一般流程可以用以下方式描述:
- 在 try-catch 块中发出请求。
- 如果没有异常,则检查响应的 statusCode。
- 如果它与预期的不同,则尝试阅读响应的正文
并记录所有内容。
第 1 步
HttpResponseMessage response = null;
try
{
response = await httpClient.PutAsync(...);
}
catch (InvalidOperationException ioEx)
{
//The request message was already sent by the HttpClient instance, but failed due to some protocol violation
HttpClient.CancelPendingRequests();
//TODO: logging
}
catch (TaskCanceledException tcEX)
{
//The request was not completed due to either it's timed out or cancelled
if(!tcEX.CancellationToken.IsCancellationRequested)
HttpClient.CancelPendingRequests();
//TODO: logging
}
catch (HttpRequestException hrEx)
{
//The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation.
//TODO: logging
}
第 2 步
HttpStatusCodes[] validResponseCodes = new [] {
HttpStatusCode.OK,
HttpStatusCode.Created,
HttpStatusCode.NoContent,
};
if(!validResponseCodes.Contains(response?.StatusCode))
{
//Step #3
}
第 3 步
string errorResponse = await response.Content.ReadAsStringAsync();
//Try to parse it if you know the structure of the returned json/xml/whatever
我正在开发 Core 3.1 Web API 和使用它的 MVC 应用程序。在 MVC 应用程序中,我 UserRepo
设置了包含向 API:
public class UserRepo : IUserRepo
{
private readonly IHttpClientFactory _clientFactory;
public UserRepo(IHttpClientFactory httpClientFactory)
{
_clientFactory = httpClientFactory;
}
public async Task<User> GetById(int Id)
{
// same code structure as Update ...
}
public async Task<User> Update(User user)
{
HttpClient client = _clientFactory.CreateClient("NamedClient");
try
{
HttpResponseMessage response = await client.PutAsync($"api/Users/{user.Id}", ContentEncoder.Encode(user));
return await response.Content.ReadFromJsonAsync<User>();
}
catch (Exception ex)
{
throw;
}
}
public async Task<User> Insert(User user)
{
// same code structure as Update ...
}
}
Update
方法从不抛出从 API 返回的 400、404 等错误,从而导致静默错误。我发现要引起异常,我需要调用 response.EnsureSuccessStatusCode();
,它起作用了。
但是,异常不包含我需要找出 API 调用出了什么问题的内容。如果发生 400 错误,将抛出一个异常,说明发生了 400 错误,但不会 why 发生。 why 返回到 response
变量,由于我已经实施了验证,它可能看起来像这样:
{
"errors": {
"FirstName": [
"The FirstName field is required."
]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|502d647b-4c7425oa321c8c7b."
}
是否有一种广泛使用的方法来处理在 API 中产生错误后返回的响应?我想知道为什么会出现 400 错误,以便我知道要修复什么。我只是不知道处理这些响应消息的“正确”方式是什么。
我的一个想法是捕获异常并在每次抛出异常之前将其与响应文本一起记录下来。然后,当我的应用程序崩溃时,我可以转到日志并阅读返回的消息。 Update
方法如下所示:
public async Task<User> Update(User user)
{
HttpClient client = _clientFactory.CreateClient("NamedClient");
HttpResponseMessage response = await client.PutAsync($"api/Users/{user.Id}", ContentEncoder.Encode(user));
try
{
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
string errorMessage = await response.Content.ReadAsStringAsync()
_logger.LogError(ex, errorMessage);
throw;
}
return await response.Content.ReadFromJsonAsync<User>();
}
另一个想法是也许可以将消息添加到异常本身并在抛出时看到它?将消息添加为内部异常有意义吗?
Is there a widely used way to handle the response that comes back after an error is produced in the API? I want to know why a 400 error occurred so I know what to fix. I just don't know what is the "right" way to handle these response messages.
通常,异常详细信息只会被记录,而不会 returned。这是因为详细信息可能包括个人身份信息或可能揭示潜在安全漏洞的技术细节。有一个 error details RFC 变得越来越普遍,但即使这样也不应该包含 PII 或堆栈跟踪等详细信息。
在一个 API(MVC 端点)调用另一个 API(实际的 API)的情况下,MVC 端点应该 return 5xx 范围。此处可以接受 500 或 502。所有此类错误及其详细信息都应记录在服务器端。
请注意,如果传播异常,默认行为是 return 500,因此您真正需要做的就是保持 throw;
。但是,在“管道”中执行错误 logging 是正常的,例如 ASP.NET Core 的中间件或 ASP.NET MVC 的全局安装的动作过滤器之类的东西.这是为了确保记录所有错误,同时避免重复。
EnsureSuccessStatusCode
如果 StatusCode
与 2xx 不同,则抛出 HttpRequestException
。
为了从响应中获取最多的信息,您必须手动检索它。
一般流程可以用以下方式描述:
- 在 try-catch 块中发出请求。
- 如果没有异常,则检查响应的 statusCode。
- 如果它与预期的不同,则尝试阅读响应的正文
并记录所有内容。
第 1 步
HttpResponseMessage response = null;
try
{
response = await httpClient.PutAsync(...);
}
catch (InvalidOperationException ioEx)
{
//The request message was already sent by the HttpClient instance, but failed due to some protocol violation
HttpClient.CancelPendingRequests();
//TODO: logging
}
catch (TaskCanceledException tcEX)
{
//The request was not completed due to either it's timed out or cancelled
if(!tcEX.CancellationToken.IsCancellationRequested)
HttpClient.CancelPendingRequests();
//TODO: logging
}
catch (HttpRequestException hrEx)
{
//The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation.
//TODO: logging
}
第 2 步
HttpStatusCodes[] validResponseCodes = new [] {
HttpStatusCode.OK,
HttpStatusCode.Created,
HttpStatusCode.NoContent,
};
if(!validResponseCodes.Contains(response?.StatusCode))
{
//Step #3
}
第 3 步
string errorResponse = await response.Content.ReadAsStringAsync();
//Try to parse it if you know the structure of the returned json/xml/whatever