如何修复.Net Core POST操作中的400错误请求错误

时间:2019-04-17 09:13:16

标签: entity-framework asp.net-core postman

我有一个.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"
}

3 个答案:

答案 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)

Chris Pratt是正确的,您需要发送__RequestVerificationToken

如果您注释掉[ValidateAntiForgeryToken]属性,则似乎是从Body-raw-JSON发送数据,那么您需要使用[FromBody]访问数据。

[HttpPost]
public async Task<IActionResult> Create([Bind("Assignee,Summary,Description")] [FromBody] TaskViewModel taskViewModel)

enter image description here

如果您不想添加[FromBody],则可以使用form-data

发送数据

enter image description here