У меня есть 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"
}





Вы должны отправить токен защиты от подделки вместе с запросом, если хотите использовать декоратор [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?
Idтребуется и отсутствует в показанной полезной нагрузке. Это приводит к тому, чтоModelState.IsValidвозвращаетfalse, а не фиксирует.