Возвратить объект вместе с ошибкой 409 Conflict в вызове POST Web API 2, поддерживаемом Entity Framework?
У меня есть С# Entity Framework Контроллер Web API 2. В настоящее время при попытке создать объект с тем же текстом для основного текстового поля с помощью метода POST я возвращаю 409 Conflict error как StatusCode, чтобы указать, что добавление считается дубликатом.
То, что я хотел бы сделать, это вернуть объект на стороне сервера, который также вызвал дублируемую ошибку. Поэтому мне нужно что-то похожее на метод Ok(), но вариант, который возвращает ошибку 409 Conflict как код состояния HTTP вместо кода состояния HTTP OK.
Есть ли такая вещь? Как я могу это сделать? Если я смогу выполнить эту работу, клиенту не придется выполнять последующий вызов Получить на сервер, чтобы получить существующий объект после получения ошибки 409 Conflict.
Здесь текущий метод POST:
public IHttpActionResult PostCanonical(Canonical canonical)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Check for duplicate Canonical text for the same app name.
if (db.IsDuplicateCanonical(canonical.AppName, canonical.Text))
{
// It a duplicate. Return an HTTP 409 Conflict error to let the client know.
return StatusCode(HttpStatusCode.Conflict);
}
db.CanonicalSentences.Add(canonical);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = canonical.ID }, canonical);
}
Ответы
Ответ 1
EDIT: это решение для WebApi до версии v5, см. этот ответ, если вы используете v5 или выше.
Вы можете вернуть NegotiatedContentResult<T>
, который позволяет указать код состояния и объект, который будет помещен в тело сообщения http.
Измените свой код на что-то вроде этого:
public IHttpActionResult PostCanonical(Canonical canonical)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Check for duplicate Canonical text for the same app name.
if (db.IsDuplicateCanonical(canonical.AppName, canonical.Text))
{
// It a duplicate. Return an HTTP 409 Conflict error to let the client know.
var original = db.CanonicalSentences.First(c => c.ID == canonical.ID);
return new NegotiatedContentResult<T>(HttpStatusCode.Conflict, original, this);
}
db.CanonicalSentences.Add(canonical);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = canonical.ID }, canonical);
}
Или, возможно, оберните его методом расширения следующим образом:
public static class HttpActionResultExtensions {
public static IHttpActionResult StatusCodeWithContent<T>(this ApiController @this, HttpStatusCode statusCode, T content) {
return new NegotiatedContentResult<T>(statusCode, content, @this);
}
}
И затем используйте расширение следующим образом:
public IHttpActionResult PostCanonical(Canonical canonical)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Check for duplicate Canonical text for the same app name.
if (db.IsDuplicateCanonical(canonical.AppName, canonical.Text))
{
// It a duplicate. Return an HTTP 409 Conflict error to let the client know.
var original = db.CanonicalSentences.First(c => c.ID == canonical.ID);
return StatusCodeWithContent(HttpStatusCode.Conflict, original)
}
db.CanonicalSentences.Add(canonical);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = canonical.ID }, canonical);
}
Ответ 2
Вы должны вернуть Content:
return Content(HttpStatusCode.Conflict, original);
Content
- это метод класса ApiController
, который создаст NegotiatedContentResult
с предоставленным HttpStatusCode
и контентом. Нет необходимости создавать свой собственный метод расширения в классе ApiController, как в принятом ответе.
Ответ 3
У меня была аналогичная проблема с ASP.NET Core 1.0 RC2, однако я столкнулся с DbUpdateConcurrencyException
, используя оптимистичный concurrency. Я не хотел, чтобы мой пользователь обновлял уже обновленный объект.
Я также хотел вернуть обновленный объект пользователю, выполняющему вызов. Я могу создать новый CreatedAtAction
и установить StatusCode
в StatusCodes.Status409Conflict
context.Entry(user).State = EntityState.Modified;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(id))
{
return NotFound();
}
else
{
//remove the user we just added, otherwise it will not goto
//the database to obtain the updated user
context.Entry(user).State = EntityState.Detached;
var updatedUser = await context.Users.SingleOrDefaultAsync(m => m.Id == id);
if (updatedUser == null)
{
return NotFound();
}
var returnAction = CreatedAtAction("PutUser", new { id = user.Id }, updatedUser);
returnAction.StatusCode = StatusCodes.Status409Conflict;
return returnAction;
}
}