Как исправить ошибку 400 Bad Request в операции .Net Core POST?

У меня есть API .Net Core 2.1, который публикует данные с использованием ядра EF. Когда я делаю POST-запрос от Postman к http://localhost:3642/task/create, я получаю ошибку 400 Bad Request Error (запрос не может быть выполнен из-за неправильного синтаксиса). Проверить токен AntiForgery с контроллера. Когда я передаю запрос от почтальона с этим изменением, я получаю сообщение о состоянии 200 Ok, но данные не фиксируются в таблице на сервере Sql. Есть ли что-то, что я должен настроить в своем 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; }
}

Это моя полезная нагрузка в Postman:

{
    "Assignee": "Ed tshuma",
    "Summary": "Finish reconciliations",
    "Description": "collate all the pending data"
}
Id требуется и отсутствует в показанной полезной нагрузке. Это приводит к тому, что ModelState.IsValid возвращает false, а не фиксирует.
Nkosi 17.04.2019 11:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
13
1
37 935
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы должны отправить токен защиты от подделки вместе с запросом, если хотите использовать декоратор [ValidateAntiForgeryToken]. См. эта ссылка для получения дополнительной информации.

Кроме того, даже если ваша модель недействительна, вы return View(). Это означает, что вы получаете статус http 200, даже если отправляете неправильные данные.

Установите точку останова на if (ModelState.IsValid) и проверьте, входите ли вы в нее. Если нет, проверьте формат полезной нагрузки.

Надеюсь, поможет.

РЕДАКТИРОВАТЬ относительно вашей полезной нагрузки и вашей модели: вам необходимо предоставить Id полезную нагрузку из-за декоратора [Required] в вашей модели TaskViewModel. Или вам нужно избавиться от атрибута [Required] на Id. Если вы этого не сделаете, if (ModelState.IsValid) всегда будет ложным.

Ответ принят как подходящий

Здесь есть ряд проблем. Прежде всего, почему вы сохраняете свою модель представления в базе данных. На самом деле в данном случае это организация, а не модель представления. Вы определенно должны использовать модель представления, но у вас также должен быть отдельный класс сущностей. Затем ваша модель представления должна содержать только те свойства, которые вы действительно хотите разрешить пользователю редактировать, полностью исключая необходимость в атрибуте Bind, которого в любом случае следует избегать. (см.: Бинд это зло).

// 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, вы должны отправить запрос как x-www-form-urlencoded. Если вы этого не сделаете, ваша модель по существу всегда будет недействительной, потому что ничего не будет привязано к вашей модели из тела сообщения.

Чтобы получить JSON, к вашему параметру должен быть применен атрибут FromBody ([FromBody]TaskViewModel model). Однако, если вы это сделаете, вы больше не сможете получать сообщения в традиционной форме, и в этом контексте это то, что будет отправлено. Если вы отправляли через AJAX (где вы могли бы использовать JSON), то вы также должны использовать возвращение JSON или, может быть, PartialView, но не View или редирект.

Наконец, вам нужно включить токен проверки запроса, который должен быть еще одним ключом в имени тела сообщения __RequestVerificationToken. Чтобы получить значение для отправки, вам нужно сначала загрузить GET-версию представления и проверить источник. Там будет скрытый ввод со значением.

Крис Пратт прав, вам нужно отправить __RequestVerificationToken.

Если вы закомментируете атрибут [ValidateAntiForgeryToken], кажется, что вы отправляете данные из Body-raw-JSON, тогда вам нужно использовать [FromBody] для доступа к данным.

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

Если вы не хотите добавлять [FromBody], вы можете отправить данные с помощью form-data

Какие у вас 2 Headers?

Serj Sagan 17.08.2020 08:36

Другие вопросы по теме