我有一个.Net Core 2.1 API,可以使用EF core发布数据。当我从Postman向http://localhost:3642/task/create发出POST请求时,收到400错误的请求错误(由于语法错误,该请求无法实现)。在四处寻找信息后,我提出了从控制器中注释掉ValidateAntiForgery令牌的建议。当我通过邮递员的更改请求时,我得到200 OK状态消息,但是没有数据提交到Sql Server中的表。我应该在我的API中配置一些东西,而我还缺少其他东西吗?
我的控制器如下所示:
[HttpPost]
// [ValidateAntiForgeryToken]
public async Task<IActionResult>
Create([Bind("Assignee,Summary,Description")] TaskViewModel taskViewModel)
{
if (ModelState.IsValid)
{
_context.Add(taskViewModel);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View();
}
在TaskViewModel.cs中,我有:
public class TaskViewModel
{
[Required]
public long Id { get; set; }
[Required(ErrorMessage = "Please provide Task Summary")]
[Display(Name = "Summary")]
public string Summary { get; set; }
[Required(ErrorMessage = "Please enter task description")]
[Display(Name = "Description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please select Assignee")]
[Display(Name = "Assign To")]
public string Assignee { get; set; }
}
这是我在邮递员中的有效载荷:
{
"Assignee": "Ed tshuma",
"Summary": "Finish reconciliations",
"Description": "collate all the pending data"
}
答案 0 :(得分:1)
这里有很多问题。首先,为什么要将视图模型保存到数据库中。在这种情况下,这实际上是一个实体,而不是视图模型。您绝对应该使用视图模型,但也应该有一个单独的实体类。然后,您的视图模型应该只包含您要实际允许用户编辑的属性,从而完全不需要使用Bind
属性,无论如何都应避免。 (请参阅:Bind is Evil)。
// added "Entity" to the name to prevent conflicts with `System.Threading.Task`
[Table("Tasks")]
public class TaskEntity
{
[Key]
public long Id { get; set; }
[Required]
public string Summary { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Assignee { get; set; }
}
public class TaskViewModel
{
[Required(ErrorMessage = "Please provide Task Summary")]
[Display(Name = "Summary")]
public string Summary { get; set; }
[Required(ErrorMessage = "Please enter task description")]
[Display(Name = "Description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please select Assignee")]
[Display(Name = "Assign To")]
public string Assignee { get; set; }
}
此外,请注意责任划分。实体只有对数据库重要的事物([Required]
在此表示该列应为非空)。而视图模型仅与视图有关。没有Id
属性,因为它不是必需的或不需要的,并且要显示给用户的显示名称和错误消息仅放在此处。
然后,您需要从视图模型映射到实体类:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(TaskViewModel model)
{
if (!ModelState.IsValid)
return View(model);
var task = new TaskEntity
{
Assignee = model.Assignee,
Summary = model.Summary,
Description = model.Description
};
_context.Add(task);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
这里的映射非常简单,但是您可能更喜欢利用AutoMapper之类的库来为您处理:_mapper.Map<TaskEntity>(model)
。
尽管这是专门针对创建动作的,但值得指出更新的细微差别。您首先要从数据库中检索现有任务,然后将发布的值映射到该任务。其余的保持相对不变:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Update(long id, TaskViewModel model)
{
if (!ModelState.IsValid)
return View(model);
var task = await _context.Tasks.FindAsync(id);
if (task == null)
return NotFound();
task.Assignee = model.Assignee;
task.Summary = model.Summary;
task.Description = model.Description;
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
最后,关于您问题中的主要问题,有两个问题。首先,此操作是针对传统HTML表单帖子(x-www-form-urlencoded
)设计的。因此,向其发送JSON没有任何意义,并且向其发送JSON将不起作用。要在Postman中进行测试,您应该以{{1}}的形式发送请求。如果您不这样做,那么您的模型实际上将始终是无效的,因为任何内容都不会从发布主体绑定到您的模型。
为了接收JSON,您的参数将需要应用x-www-form-urlencoded
属性(FromBody
)。但是,如果这样做,您将无法再接收传统的表单帖子,在这种情况下,这就是要发送的内容。如果您通过AJAX发送(可以使用JSON),那么您还应该返回 JSON或返回[FromBody]TaskViewModel model
,但不能返回PartialView
或重定向。>
最后,您需要包括请求验证令牌,该令牌应该是帖子正文名称View
中的另一个关键字。要获取要发送的值,您需要先加载视图的GET版本,然后检查源。将带有值的隐藏输入。
答案 1 :(得分:0)
如果要使用装饰器[ValidateAntiForgeryToken]
,则必须在请求中发送防伪令牌。有关更多信息,请参见this link。
此外,即使您的模型无效,您也return View()
。这意味着即使您发送错误的数据,您也会获得http状态200。
在if(ModelState.IsValid)
上设置一个断点,并检查是否在其中输入。如果没有,请检查有效载荷的格式。
希望有帮助。
关于有效负载和模型的 编辑:由于TaskViewModel中有Id
装饰器,因此需要为有效负载提供[Required]
。否则,您需要摆脱[Required]
上的Id
属性。否则,if (ModelState.IsValid)
将始终为假。
答案 2 :(得分:0)