Подтвердить что ты не робот

Возвратить объект вместе с ошибкой 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);
    }
4b9b3361

Ответ 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;                    
    }
}