永远不要直接从请求中保存任何内容。
这是你的最佳实践。前面的代码之所以优越,唯一的原因是您明确选择了实际持久化的发布值,而不是盲目地保存用户决定直接发送到数据库的任何内容。
更好的方法是实际使用视图模型来接受用户输入。例如:
public class MessageViewModel
{
public int OwnerId { get; set; }
[Required]
public string Text { get; set; }
}
然后,将其作为参数接受,并将其映射到实体:
public async Task<IActionResult> Post([FromBody]MessageViewModel model)
{
if (ModelState.IsValid)
{
var message = new Message
{
Owner = db.Users.Find(model.OwnerId),
Text = model.Text
};
db.Messages.Add(message);
await db.SaveChangesAsync();
return Ok(message);
}
return BadRequest(ModelState);
}
我在上述代码中做了一些重要更改:
-
添加了一个条件来检查
ModelState.IsValid
在继续保存之前为true。正如您现在看到的,您只是盲目地尝试保存传入的任何内容,即使这样做最终会引发数据库异常,因为缺少必需的值等等。
-
有了这个条件,您现在需要处理出现问题的场景。更有可能的是,您希望返回某种错误对象,以帮助客户机修复问题。通常只回来
ModelState
,它将序列化其验证错误列表,但您可能还想做其他事情。
-
由于这现在可能需要返回两种不同的对象类型,因此操作签名已更改为返回
IActionResult
. 通常最好一直使用这种返回类型。基本上,这是一种包罗万象的回报,而具体回报如下
Message
很容易就失去了它的实用性,需要你继续做下去,然后再改变它。实际上,您应该只在绝对没有任何失败机会的情况下返回特定类型,即响应将
总是
200好吧。然而,这种情况很少发生。
-
视图模型只接受所有者的id,而不是完整的(大概是
ApplicationUser
)对象。接受
消息
保存它也直接存在于这里。恶意用户可以使用
Owner
对象属性,允许它们潜在地更改所有不应更改的内容。通常,您应该始终允许用户操作尽可能少的数据。任何你暴露的事情都应该做
故意地
,充分理解其含义。由于我们现在只接受一个id,因此我们需要查找具有该id的用户,以设置
物主
实体上的属性。如果碰巧有一个显式外键属性,例如
OwnerId
在…上
消息
,你可以直接设置。
-
改变
AddAsync
只是
Add
. 根据文档,您几乎不应该使用
添加异步
-
补充
await
到
SaveChangesAsync
呼叫始终等待异步操作,除非操作的完成与任何事情无关。这里绝对不是这样。
保存更改同步
可以引发应用程序需要处理的异常。不等待它,基本上只是吞下这些,让代码愉快地继续,好像没有问题。虽然这听起来可能是积极的,但绝对不是。不等待还可能导致其他问题,例如上下文可能在调用完成之前被处理。
-
既然我们现在在等待
保存更改同步
async
必须添加到方法签名,并且必须返回
Task<IActionResult>