Return an object along with a 409 Conflict error in a Web API 2 POST call backed by Entity Framework?

32,465

Solution 1

EDIT: This solution is for WebApi prior v5, please see this answer if you are using v5 or above.

You could return a NegotiatedContentResult<T> that lets you specify the status code and an object to be put into the http message body.

Change your code to something like 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's 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);
}

Or maybe wrap it an extension method like this:

public static class HttpActionResultExtensions {
    public static IHttpActionResult StatusCodeWithContent<T>(this ApiController @this, HttpStatusCode statusCode, T content) {
        return new NegotiatedContentResult<T>(statusCode, content, @this);
    }
}

And then use the extension like 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's 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);
}

Solution 2

You should return Content:

return Content(HttpStatusCode.Conflict, original);

Content is method on the ApiController class which will create a NegotiatedContentResult with the provided HttpStatusCode and content. There is no need to create your own extension method on the ApiController class like in the accepted answer.

Solution 3

Arrived here looking for help with ASP.NET Core HTTP 409 - this is related, just the newer approach to solving this same problem.

Conflict ActionResult

 return Conflict(new { message = $"An existing record with the id '{id}' was already found."});
Share:
32,465

Related videos on Youtube

Robert Oschler
Author by

Robert Oschler

Newly minted, avid Windows Phone developer with a passion for natural language processing apps that leverage Azure for a complete, intelligent, mobile client platofrm. Also a devout robotics enthusiast and consumer EEG headset applicaitons developer (Emotiv EPOC headset).

Updated on August 03, 2022

Comments

  • Robert Oschler
    Robert Oschler over 1 year

    I have a C# Entity Framework Web API 2 controller. Currently when an attempt is made via the POST method to create an object with the same text for the main text field, I return a 409 Conflict error as an StatusCode result to indicate the addition is considered a duplicate.

    What I'd like to do is return the server side object that triggered the duplicate error too. So I need something akin to the Ok() method but a variant that returns a 409 Conflict error as the HTTP status code instead of an HTTP OK status code.

    Is there such a thing? How can I do this? If I can make this work the client doesn't have to do a subsequent Get call to the server to get the existing object after receiving a 409 Conflict error.

    Here's the current POST method:

        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's 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);
        }
    
  • Robert Oschler
    Robert Oschler over 8 years
    Thanks. That's two useful solutions I didn't think of it. Nice and simple too.
  • Anish Patel
    Anish Patel over 8 years
    No problem buddy, glad I could help
  • Adrian Sanguineti
    Adrian Sanguineti over 6 years
    This will work but is unnecessary in the current version of WebApi. Just return Content(HttpStatusCode.Conflict, original) (a built in method) to get the same result. See answer by @Kieran.
  • Anish Patel
    Anish Patel over 6 years
    @Adrian cool, what version was the Content method introduced is it in Core?
  • Adrian Sanguineti
    Adrian Sanguineti over 6 years
    I haven't been able to work out when it was introduced at this time but it appears to have been in the Microsoft.AspNet.WebApi.Core nuget package package at least since version 5.0.0 (10/17/2013). I haven't done much ASP.NET Core, but I believe the equivalent is StatusCode(int, Object)
  • Dave
    Dave about 5 years
    Thank you, this is what I need!