How to Fix Bad Request Error ASP.NET Core

This article will explain other error that you can meet when deploying Asp.net core. The common error like 502.5 Process Failure we have discussed it on previous article. This continuation of other error that you can find when deploying Asp.net core

Solution

There’s a number of issues here. First and foremost, why are you saving your view model to the database. This is actually an entity in this case, not a view model. You should definitely be using a view model, but you should also have a separate entity class. Then, your view model should only contain properties that you want to actually allow the user to edit, negating the need entirely for the Bind attribute, which should be avoided anyways. (see: 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; }
}

Also, note the division of responsibility. The entity has only things that matter to the database ([Required] here indicates that the column should be non-nullable). Whereas the view model is concerned only with the view. There’s no Id property, since it’s not needed or desired, and the display names and error messages to be presented to the user are placed here only.

Then, you’ll need to map from your view model to your entity class:

[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");
}

The mapping here is fairly straight-forward, but you may prefer to utilize a library like AutoMapper to handle this for you: _mapper.Map<TaskEntity>(model).

While this is specifically for a create action, it’s worth pointing out the subtle difference for an update. You’ll want to first retrieve the existing task from your database and then map the posted values onto that. The rest remains relatively the same:

[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");
}

Finally, as to the main problem from your question, there’s two issues. First, this action is designed for a traditional HTML form post (x-www-form-urlencoded). As such, it doesn’t make sense to send JSON to it, and sending JSON to it will not work. To test it in Postman, you should send the request as x-www-form-urlencoded. If you do not, then your model will essentially always be invalid, because nothing will be bound to your model from the post body.

In order to receive JSON, your param would need to have the FromBody attribute applied to it ([FromBody]TaskViewModel model). However, if you do that, you can no longer receive traditional form posts, and in this context, that’s what’s going to be sent. If you were sending via AJAX (where you could conceivably use JSON), then you should also be returning JSON or maybe PartialView, but not View or a redirect.

Lastly, you need to include the request verification token, which should be another key in the post body name __RequestVerificationToken. To get the value to send, you’ll need to load the GET version of the view, first, and inspect the source. There will be a hidden input with the value.

Then, you need to use __RequestVerificationToken.

If you comment out [ValidateAntiForgeryToken] attribute, it seems that you send data from Body-raw-JSON, then you need to use [FromBody] to access data.

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

If you do not want to add [FromBody], you could send data using form-data

Conclusion

We hope above article can help you to fix 400 bad request above. Stay tune in next interesting article. Happy coding!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *