首先,抱歉我的英语不好。我正在开发一个 .Net Core Rest Api(之前我已经在 asp.net mvc 中开发过,但在 net core 中是第一次),最近遇到了一个奇怪的问题。
应用程序分为多个层:WebApi -> Core <- Infrastructure。
问题在于,在核心层的服务中,我决定针对某些业务规则抛出一些异常。例如,有一些可翻译的实体,当您想要创建新实体时,必须为应用程序中定义的每种文化提供翻译。如果缺少翻译,我会向上抛出一个异常。
下面是一个方法的示例:
我在想如何处理控制器中的异常,以便根据每种异常类型发送适当的httpStatusCode和可读的消息给客户端。我发现了三种主要方法: - 在控制器方法中使用Try/Catch - 自定义异常过滤器 - 自定义异常中间件
我决定选择中间件,因为它似乎是最好的方法,在整个应用程序中拥有一个集中处理异常并向客户端发送消息的地方,所以我实现了这个功能:
然后我将中间件连接到了管道中:
在这个阶段,一切都很完美,但是我刚刚注意到当中间件处理异常时,响应时间远远不够理想。这是使用中间件的响应时间:
测试了多次,平均响应时间为460ms + -。我认为一定有一种更快的方法,于是我尝试了另外两种方法。使用异常过滤器属性:
应用程序分为多个层:WebApi -> Core <- Infrastructure。
问题在于,在核心层的服务中,我决定针对某些业务规则抛出一些异常。例如,有一些可翻译的实体,当您想要创建新实体时,必须为应用程序中定义的每种文化提供翻译。如果缺少翻译,我会向上抛出一个异常。
下面是一个方法的示例:
public async Task<CompanyResource> Add(CompanyResource companyResource)
{
var cultures = await _cultureRepository.GetAllAsync();
if (cultures.Any(culture => companyResource.Translations.All(t => t.CultureCode != culture.Code)))
throw new MissingTranslationsException();
await _companyResourceRepository.AddAsync(companyResource);
return companyResource;
}
我在想如何处理控制器中的异常,以便根据每种异常类型发送适当的httpStatusCode和可读的消息给客户端。我发现了三种主要方法: - 在控制器方法中使用Try/Catch - 自定义异常过滤器 - 自定义异常中间件
我决定选择中间件,因为它似乎是最好的方法,在整个应用程序中拥有一个集中处理异常并向客户端发送消息的地方,所以我实现了这个功能:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var code = HttpStatusCode.InternalServerError;
if (exception is InvalidPermissionsException) code = HttpStatusCode.Forbidden;
else if (exception is DuplicateEntityException) code = HttpStatusCode.Conflict;
else if (exception is MissingTranslationsException) code = HttpStatusCode.BadRequest;
else if (exception is ApplicationException) code = HttpStatusCode.BadRequest;
var result = JsonConvert.SerializeObject(new []{ exception.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
await context.Response.WriteAsync(result);
}
}
然后我将中间件连接到了管道中:
最初的回答
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...other code
app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseHttpsRedirection();
app.UseMvc();
}
在这个阶段,一切都很完美,但是我刚刚注意到当中间件处理异常时,响应时间远远不够理想。这是使用中间件的响应时间:
![enter image description here](https://istack.dev59.com/EXdzT.webp)
public class HandleExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var code = HttpStatusCode.InternalServerError;
var exception = context.Exception;
if (exception is InvalidPermissionsException) code = HttpStatusCode.Forbidden;
else if (exception is DuplicateEntityException) code = HttpStatusCode.Conflict;
else if (exception is MissingTranslationsException) code = HttpStatusCode.BadRequest;
else if (exception is ApplicationException) code = HttpStatusCode.BadRequest;
context.HttpContext.Response.StatusCode = (int)code;
context.Result = new JsonResult(new[] { exception.Message });
}
}
使用过滤器时,响应时间平均降至250毫秒左右。但我仍觉得这太慢了。因此,我选择了最后一个选项:在操作方法内部使用try catch:
Original Answer翻译成"最初的回答"
[HttpPost]
public async Task<IActionResult> Post([FromBody] CompanyResourceDto companyResourceDto)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var companyresource = _mapper.Map<CompanyResource>(companyResourceDto);
try
{
companyresource = await _resourcesService.Add(companyresource);
}
catch (Exception e)
{
return BadRequest(new[] {e.Message});
}
return CreatedAtRoute("Getresource", new { id = companyresource.Id }, _mapper.Map<CompanyResourceDto>(companyresource));
}
同时,在出现异常的情况下,平均响应时间下降到了85毫秒+-(这是我从一开始就期望的,通过中间件实现)。
我认为,为了易于维护和关注点分离,使用中间件是最好的选择,因为我不希望我的控制器意识到除了正常流程之外的任何事情。
所以,我的问题是:
我在中间件方面做错了什么吗? 如果没有,为什么会如此缓慢,考虑到响应时间对该应用程序至关重要,我应该采取什么方法? 如果有,我应该改变什么?
谢谢和问候。
最初的回答:
try / catch
之后检查它。 - Jeroen Mostert