我想避免使用重复的try / catch块,重复的日志记录命令,并考虑HTTP响应代码(例如404、200、204等),并将API方法的代码行最小化到某些接口或服务中。换句话说,使我的代码更干燥。
给出以下代码:
[HttpGet()]
[Route("Contracts/{id}")]
public async Task<IActionResult> Get(int id)
{
try
{
var results = await _service.GetContractByIdAsync(id);
if (results == null) { return NotFound(); }
return Ok(results);
}
catch(Exception ex)
{
_log(ex);
return StatusCode(500);
}
}
如果我有多种类似的方法,则除了以下每一行代码
var results = await _service.GetContractByIdAsync(id);
将重复。如何避免重复?也许以后我想改变我处理错误的方式,并且我不想在很多地方都改变它。
答案 0 :(得分:1)
您可以定义一个ResultFilterAttribute
,它将所有具有空值的ObjectResult
更改为NotFoundResult
。您可以在任何适用的地方注释控制器方法,甚至可以全局注册过滤器。另请参阅文章Convert Null Valued Results to 404 in asp.net-core mvc,了解如何以及为什么。
属性看起来像这样(所述文章的代码)
public class NotFoundResultFilterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult objectResult && objectResult.Value == null)
{
context.Result = new NotFoundResult();
}
}
}
将其应用于您的控制器方法,然后返回结果。
[HttpGet()]
[Route("Contracts/{id}")]
[NotFoundResult]
public async Task<IActionResult> Get(int id) {
var results = await _service.GetContractByIdAsync(id);
return results;
}
确保您的控制器装饰有ApiController
属性。我省略了try-catch-block,因为您还可以让管道为您处理异常。在您的Startup.Configure
方法中,为app.UseExceptionHandler(...)
注册一个lambda。
有关详细信息,请查看Microsoft ASP.NET Core文档-section error handling。
答案 1 :(得分:1)
挑战1:尝试/抓住
您可以在MVC之前添加middleware,这将在try / catch块中执行下一个中间件(包括MVC):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
app.UseMyExceptionHandler();
// ...
app.UseMvc();
}
中间件可能看起来像这样:
public class ExceptionHandlerMiddleware
{
readonly RequestDelegate _next;
readonly ILogger logger;
public ExceptionHandlerMiddleware(RequestDelegate next, ILogger<ExceptionHandlerMiddleware> logger)
{
_next = next;
this.logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception e)
{
await HandleExceptionAsync(context, e);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
// Log the exceptions
string result = ... cretate the response if you need it
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
}
然后我使用扩展方法将其添加到应用构建器中:
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseMyExceptionHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
}
}
现在,您可以从所有控制器操作中删除try/catch
,因为所有未处理的异常都将被中间件捕获。
还有一些built-in ways可以帮助您的事情。
挑战2:对NotFound进行空检查
您可以创建一些所有控制器都将继承的基本控制器,并处理其中的null
检查:
public class BaseController : ControllerBase
{
protected IActionResult CreateResponse(object result)
{
if (results == null)
return NotFound();
return Ok(results);
}
}
,然后使所有控制器都继承自它:
[ApiController]
public class YourController : BaseController
// ...
现在您的操作方法可能如下所示:
[HttpGet()]
[Route("Contracts/{id}")]
public async Task<IActionResult> Get(int id)
{
var results = await _service.GetContractByIdAsync(id);
return CreateResponse(results);
}