【发布时间】:2020-10-27 21:35:28
【问题描述】:
我正在创建一个 dotnet core 3.1 API 以在现有系统中工作。 [dbo].[MyTable] 表有两个具有唯一约束的字段:[ID] 和 [HumanReadableID]。 [ID] 是一个 GUID,将在发布新资源时由 API 确定。 [HumanReadableID] 必须由用户确定,因此 API 在执行插入之前必须首先通过查询数据库来确保其唯一性。
我尝试使用自定义属性进行此验证,并在控制器方法接受的 DTO 上实现 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);
}
}
}
这部分可行,但由此产生的错误的 Content-Type 为 application/json 而不是 application/problem+json。此外,这只是感觉控制器在这里承担了太多责任。
我使用 IValidatableObject 或 DataAnnotations 执行的验证缺少什么?如果做不到这一点,我必须进一步操作控制器的 ModelState 中的哪些内容才能让它发出符合标准的谴责?
【问题讨论】:
-
我更喜欢将
IValidatableObject定义为“对象数据本身是有效的”。有了这个定义,对象本身是有效的——我们只是不能保存它。另外我认为 dtos 应该是 POCO,你永远不应该在这里使用依赖注入。 -
@ChristophLütjen 谢谢。事实上,我也有同样的感受(虽然我没有能力将这些感受用语言表达出来,所以也谢谢你)。然后你会说这种检查 BELONGS 在控制器中吗?我仍然觉得我已经开始的范式可能会失控,根据捕获各种自定义异常来改变回报。 PUT 和 PATCH 动词也必须实现同样的逻辑。
-
关于“控制器是正确的位置”,我认为这是通常的“它取决于”。如果你说“动作就是我处理用例的地方”,是的——为什么不呢。您可以添加外观服务或考虑结果对象以简化操作(
var result = myServicec.Insert(obj); return this.MyConvertInternalResultObjectToErrorDtoHelper(result);- 但您也可以定义应在控制器之外处理用例,例如中介模式 github.com/jbogard/MediatR
标签: c# validation asp.net-core asp.net-apicontroller