C# 如何(以及在何处)根据数据库验证API输入
我正在创建一个dotnet core 3.1 API,以便在现有系统中工作。[dbo].[MyTable]表有两个具有唯一约束的字段:[ID]&[HumanReadableID]。[ID]是在发布新资源时由API确定的GUID。[HumanReadableID]必须由用户确定,因此API必须首先通过在执行插入之前查询数据库来确保其唯一性 我曾尝试使用自定义属性进行此验证,并在controller方法接受的DTO上实现C# 如何(以及在何处)根据数据库验证API输入,c#,validation,asp.net-core,asp.net-apicontroller,C#,Validation,Asp.net Core,Asp.net Apicontroller,我正在创建一个dotnet core 3.1 API,以便在现有系统中工作。[dbo].[MyTable]表有两个具有唯一约束的字段:[ID]&[HumanReadableID]。[ID]是在发布新资源时由API确定的GUID。[HumanReadableID]必须由用户确定,因此API必须首先通过在执行插入之前查询数据库来确保其唯一性 我曾尝试使用自定义属性进行此验证,并在controller方法接受的DTO上实现IValidatableObject,但我无法将我的存储库注入这两个对象中的任何
IValidatableObject
,但我无法将我的存储库注入这两个对象中的任何一个
目前,控制器使用存储库尝试插入,如果[HumanReadableID]不唯一,则会引发自定义异常,然后。这是密码
[Route("[controller]")]
[ApiController]
public class MyThingsController : ControllerBase
{
/// Other methods
[HttpPost(Name = "CreateMyThing")]
public async Task<IActionResult> CreateMyThing(MyThingCreateDto myThingToCreate)
{
try
{
MyThingEntity entityToCreate = _mapper.Map<MyThingEntity>(myThingToCreate);
MyThingEntity entityToReturn = await _myRepository.InsertThing(entityToCreate);
MyThingDto myThingToReturn = _mapper.Map<MyThingDto>(entityToReturn);
return CreatedAtRoute(
"GetMyThing",
new {id = myThingToReturn.Id},
myThingToReturn);
}
catch (DuplicateHumanReadableIdException e)
{
ModelState.AddModelError("HumanReadableID",$"HumanReadableID {entityToCreate.HumanReadableId} is already used by site {e.IdOfThingWithHumanReadableId}");
return Conflict(ModelState);
}
}
}
[路由(“[控制器]”)]
[ApiController]
公共类控制器:ControllerBase
{
///其他方法
[HttpPost(Name=“CreateMyThing”)]
公共异步任务CreateMyThing(MythingCreatedToMythingToCreate)
{
尝试
{
MyThingEntity entityToCreate=\u mapper.Map(myThingToCreate);
MyThingEntity entityToReturn=wait_myRepository.InsertThing(entityToCreate);
MyThingDto myThingToReturn=\u mapper.Map(entityToReturn);
返回CreatedAtRoute(
“获取神话”,
新建{id=myThingToReturn.id},
神话(回归);
}
捕获(DuplicateHumanReadableIdeException e)
{
ModelState.AddModelError(“HumanReadableID”,$“HumanReadableID{entityToCreate.HumanReadableID}已被站点{e.IdOfThingWithHumanReadableId}使用”);
返回冲突(ModelState);
}
}
}
这部分是可行的,但由此产生的错误的内容类型是application/json
,而不是application/problem+json
。此外,这让人感觉控制器在这里的责任太大了
我使用IValidatableObject或DataAnnotations进行验证的实现缺少什么?否则,在控制器的ModelState中,我必须进一步操作什么以允许其发出符合标准的谴责?关于如何实现IValidatableObject,您可以如下所示:
public class ValidatableModel : IValidatableObject
{
public int Id { get; set; }
public int HumanReadableID { get; set; }
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var service = validationContext.GetService<ICheckHumanReadableID>();
var flag = service.IfExsitHumanId(((ValidatableModel)validationContext.ObjectInstance).HumanReadableID);
if(flag)
{
yield return new ValidationResult(
$"The HumanReadableID is not unique.",
new[] { nameof(HumanReadableID) });
}
}
}
服务:
public interface ICheckHumanReadableID
{
bool IfExsitHumanId(int id);
}
public class CheckHumanReadableID : ICheckHumanReadableID
{
private readonly YourDbContext _context;
public CheckHumanReadableID(YourDbContext context)
{
_context = context;
}
public bool IfExsitHumanId(int id)
{
var list = _context.Human.Select(a => a.Id).ToList();
var flag = list.Contains(id);
return flag;
}
}
试验方法:
[HttpPost]
public async Task<ActionResult<ValidatableModel>> PostValidatableModel([FromForm]ValidatableModel validatableModel)
{
if(ModelState.IsValid)
{
_context.ValidatableModel.Add(validatableModel);
await _context.SaveChangesAsync();
return CreatedAtAction("GetValidatableModel", new { id = validatableModel.Id }, validatableModel);
}
return BadRequest(ModelState);
}
[HttpPost]
公共异步任务我更喜欢将IValidatableObject
定义为“对象数据本身有效”。根据这个定义,对象本身是有效的-我们只是不能保存它。我还认为DTO应该是POCO,你永远不应该在这里使用依赖注入。@ChristophLütjen谢谢。事实上,我也有同样的感受(尽管我不知道如何用语言表达这些感受,所以也谢谢你)。那么你会说这种检查属于控制器吗?我仍然觉得我开始的范例可能会失控,在捕获各种自定义异常的基础上改变回报。对于PUT和PATCH动词,也必须实现同样的逻辑。关于“控制器是否在正确的位置”,我认为通常是“视情况而定”。如果你说“动作就是我处理用例的地方”,是的,为什么不呢。您可以添加门面服务或考虑结果对象来简化操作(var result=myservicecc.Insert(obj);返回此.myconvertineralResultObjectToErrordToHelper(结果)
-但是你也可以定义用例应该在控制器之外处理,例如中介模式哦!我认为这里的关键是,我可以在实现IValidatableObject
时通过验证上下文从IoC容器中获取依赖项!就是这样。如果我的回答对你有帮助,请告诉我所以接受作为答案?参考:。谢谢。
[HttpPost]
public async Task<ActionResult<ValidatableModel>> PostValidatableModel([FromForm]ValidatableModel validatableModel)
{
if(ModelState.IsValid)
{
_context.ValidatableModel.Add(validatableModel);
await _context.SaveChangesAsync();
return CreatedAtAction("GetValidatableModel", new { id = validatableModel.Id }, validatableModel);
}
return BadRequest(ModelState);
}
public class YourDbContext: DbContext
{
public YourDbContext(DbContextOptions<YourDbContext> options)
: base(options)
{
}
public DbSet<ValidatableModel> ValidatableModel { get; set; }
public DbSet<Human> Human { get; set; }
}
services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("YourConnectionString")));
services.AddScoped<ICheckHumanReadableID, CheckHumanReadableID>();