C# ASP.NET核心API控制器方法中的存储库结果和状态代码
我有一个ASP.NET核心API应用程序,我正在使用C# ASP.NET核心API控制器方法中的存储库结果和状态代码,c#,asp.net-core,asp.net-core-webapi,asp.net-apicontroller,http-response-codes,C#,Asp.net Core,Asp.net Core Webapi,Asp.net Apicontroller,Http Response Codes,我有一个ASP.NET核心API应用程序,我正在使用IActionResult作为返回类型。例如,我将使用以下来自的代码段: 我的控制器与此类似,其中路由使用IActionResult,并使用诸如NotFound()、BadRequest()或Ok()等函数,并遵循存储库模式从数据库获取数据。在微软的小例子中,他们只是简单地评估结果是否为null,如果是,他们将返回NotFound() 在存储库逻辑中,可能会出现任意数量的问题。例如: DB查询成功,但未找到产品(404未找到) 数据库查询失败
IActionResult
作为返回类型。例如,我将使用以下来自的代码段:
我的控制器与此类似,其中路由使用IActionResult
,并使用诸如NotFound()
、BadRequest()
或Ok()
等函数,并遵循存储库模式从数据库获取数据。在微软的小例子中,他们只是简单地评估结果是否为null
,如果是,他们将返回NotFound()
在存储库逻辑中,可能会出现任意数量的问题。例如:
- DB查询成功,但未找到产品(404未找到)
- 数据库查询失败,连接字符串无效(500服务器错误)
- 产品ID未通过某些验证测试(400错误请求)
在上述所有情况下,您可以证明从
TryGetProduct()
函数返回null
是正确的,因为在这些情况下,它都无法检索产品。但是从存储库函数返回null
作为一般返回结果似乎没有什么帮助,可能意味着出现了很多问题我可以从存储库返回数据以允许控制器确定要返回的适当状态代码的最佳方式是什么?这方面的最佳做法是什么?我想展示一下我的方式
您可以尝试配置异常处理程序
创建新类ExceptionMiddlewareExtension,如:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using System;
using System.Net;
namespace Project.Middleware
{
public static class ExceptionMiddlewareExtension
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
ResponseModel<string> responseModel = new ResponseModel<string>();
if(contextFeature.Error is Exception)
{
responseModel.Errors.Add(new ErrorModel()
{
ErrorCode = context.Response.StatusCode.ToString(),
Message = contextFeature.Error.Message
});
}
responseModel.Result = false;
await context.Response.WriteAsync(responseModel.ToString());
}
});
});
}
}
}
然后在Startup.cs中添加以下行以配置()
另一方面,要检查您的请求参数是否有效,您可以尝试ActionFilterAttribute,如:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Project.Middleware
{
public class ModelStateValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new UnprocessableEntityObjectResult(context.ModelState);
}
}
}
}
简单地说,我使用这种方式,而且非常清楚。您的所有请求都将通过过滤器和中间件。只需在管理器中使用Try-Catch(或检查对象是否为null),如果出现问题,则抛出新异常(msg)
您还可以编写自定义异常模型并抛出。然后将模型添加到中间件以检查contextFeature.Error为CustomException
我希望这会有所帮助。我认为在数据库中找不到结果时,返回null是合适的。由于错误的查询字符串而失败的DB应该抛出一个异常,默认中间件应该捕获该异常并生成相应的500。产品ID的验证错误实际上是您应该考虑的唯一用例。您可能会使用数据注释:返回null是您所能做的最糟糕的事情。您的示例问题使至少一个潜在问题变得显而易见(为什么为空?)。例如,对于第一个示例,从
GET
返回一个Id为-1的产品。对于另外两个,您可以创建一个更复杂的对象,该对象能够封装验证的概念,该概念可以具有结果值或验证错误列表。在任何情况下,发出连接字符串或其他DB错误都不是一个好主意。当找不到对象时,返回null就可以了。如果有错误,您应该抛出一个异常,可能还有一个不同的http代码。另一方面,使用诸如返回id为-1的对象之类的黑客手段肯定不是一个好主意,因为理论上一个对象的id可能为-1。我通常做的还有一个DataResult对象,它是泛型的,有一个Content属性、一个Success属性和一个Exception属性。这样,调用者就可以很容易地知道什么时候出错了。我认为您应该在控制器方法和存储库之间放置一个业务层,并将控制器方法视为第三方(GUI或其他)的接口。因此,只需验证为该方法提供的参数。在您的业务层内执行您的逻辑,从数据库加载数据等。在您的业务层内,您可以处理有关程序逻辑的所有需要。因此,在某些情况下,NULL可能是有效值,而在其他情况下则不是。从您的业务层抛出异常,并根据您的需要在控制器内处理它们。您正在尝试获取TryGetProduct
以猜测控制器操作想要做什么。首先,TryGetProduct
与遵循TryXXX
约定的所有方法一样,在失败时不会返回任何内容,因此其out变量被设置为默认值,不管是什么,并且不应使用。但是,一个简单的Try
无法返回多个失败结果。如果要返回多个结果,应使用不同的返回类型,例如枚举、值元组(例如(结果、消息)
)和有效负载或故障消息,以及结果
类型和有效负载和解释。
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Project.Domain
{
public class ResponseModel<T>
{
public ResponseModel()
{
Errors = new List<ErrorModel>();
}
public ResponseModel(T data)
{
Data = data;
Result = true;
}
public ResponseModel(List<ErrorModel> errors)
{
Errors = errors;
}
public bool Result { get; set; }
public T Data { get; set; }
public List<ErrorModel> Errors { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
using Newtonsoft.Json;
namespace Projec.Domain
{
public class ErrorModel
{
public string Message { get; set; }
public string ErrorCode { get; set; }
public string Code { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
app.ConfigureExceptionHAndler();
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Project.Middleware
{
public class ModelStateValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new UnprocessableEntityObjectResult(context.ModelState);
}
}
}
}