458

I have concerns on the way that we returns errors to client.

Do we return error immediately by throwing HttpResponseException when we get an error:

public void Post(Customer customer)
{
    if (string.IsNullOrEmpty(customer.Name))
    {
        throw new HttpResponseException("Customer Name cannot be empty", HttpStatusCode.BadRequest) 
    }
    if (customer.Accounts.Count == 0)
    {
         throw new HttpResponseException("Customer does not have any account", HttpStatusCode.BadRequest) 
    }
}

Or we accumulate all errors then send back to client:

public void Post(Customer customer)
{
    List<string> errors = new List<string>();
    if (string.IsNullOrEmpty(customer.Name))
    {
        errors.Add("Customer Name cannot be empty"); 
    }
    if (customer.Accounts.Count == 0)
    {
         errors.Add("Customer does not have any account"); 
    }
    var responseMessage = new HttpResponseMessage<List<string>>(errors, HttpStatusCode.BadRequest);
    throw new HttpResponseException(responseMessage);
}

This is just a sample code, it does not matter either validation errors or server error, I just would like to know the best practice, the pros and cons of each approach.

Guido Leenders
  • 4,232
  • 1
  • 23
  • 43
cuongle
  • 74,024
  • 28
  • 151
  • 206
  • 1
    See http://stackoverflow.com/a/22163675/200442 you should be using `ModelState`. – Daniel Little Mar 04 '14 at 06:31
  • 1
    Note that answers here only cover Exceptions that are thrown in the controller itself. If your API returns an IQueryable that has not been executed yet, the exception is not in the controller and is not caught... – Jess Aug 26 '15 at 16:38
  • 4
    Very nice question but somehow I'm not getting any constructor overload of `HttpResponseException` class which takes two parameters mentioned in your post - `HttpResponseException("Customer Name cannot be empty", HttpStatusCode.BadRequest)` i.e. `HttpResponseException(string, HttpStatusCode)` – RBT Dec 13 '16 at 06:40

14 Answers14

344

For me I usually send back an HttpResponseException and set the status code accordingly depending on the exception thrown and if the exception is fatal or not will determine whether I send back the HttpResponseException immediately.

At the end of the day it's an API sending back responses and not views, so I think it's fine to send back a message with the exception and status code to the consumer. I currently haven't needed to accumulate errors and send them back as most exceptions are usually due to incorrect parameters or calls etc.

An example in my app is that sometimes the client will ask for data, but there isn't any data available so I throw a custom NoDataAvailableException and let it bubble to the Web API app, where then in my custom filter which captures it sending back a relevant message along with the correct status code.

I am not 100% sure on what's the best practice for this, but this is working for me currently so that's what I'm doing.

Update:

Since I answered this question a few blog posts have been written on the topic:

https://weblogs.asp.net/fredriknormen/asp-net-web-api-exception-handling

(this one has some new features in the nightly builds) https://learn.microsoft.com/archive/blogs/youssefm/error-handling-in-asp-net-webapi

Update 2

Update to our error handling process, we have two cases:

  1. For general errors like not found, or invalid parameters being passed to an action we return a HttpResponseException to stop processing immediately. Additionally for model errors in our actions we will hand the model state dictionary to the Request.CreateErrorResponse extension and wrap it in a HttpResponseException. Adding the model state dictionary results in a list of the model errors sent in the response body.

  2. For errors that occur in higher layers, server errors, we let the exception bubble to the Web API app, here we have a global exception filter which looks at the exception, logs it with ELMAH and tries to make sense of it setting the correct HTTP status code and a relevant friendly error message as the body again in a HttpResponseException. For exceptions that we aren't expecting the client will receive the default 500 internal server error, but a generic message due to security reasons.

Update 3

Recently, after picking up Web API 2, for sending back general errors we now use the IHttpActionResult interface, specifically the built in classes for in the System.Web.Http.Results namespace such as NotFound, BadRequest when they fit, if they don't we extend them, for example a NotFound result with a response message:

public class NotFoundWithMessageResult : IHttpActionResult
{
    private string message;

    public NotFoundWithMessageResult(string message)
    {
        this.message = message;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotFound);
        response.Content = new StringContent(message);
        return Task.FromResult(response);
    }
}
Callum Watkins
  • 2,844
  • 4
  • 29
  • 49
gdp
  • 8,032
  • 10
  • 42
  • 63
  • 1
    Thank you for your answer geepie, it is a good experience, so you prefer to send expcetion immediately? – cuongle May 30 '12 at 14:57
  • 1
    As i said it really depends on the exception. A fatal exception such as for example the user passes the Web Api an invalid parameter to an endpoint, then i would create a HttpResponseException and return it straight away to the consuming app. – gdp May 31 '12 at 11:06
  • 1
    The exceptions in the question are really more about validation see http://stackoverflow.com/a/22163675/200442. – Daniel Little Mar 04 '14 at 06:56
  • 2
    @DanielLittle Reread his question. I quote: "This is just a sample code, it does not matter either validation errors or server error" – gdp Mar 04 '14 at 13:46
  • 2
    @gdp Even so there really are two components to it, validation and exceptions, so it's best to cover both. – Daniel Little Mar 04 '14 at 23:18
  • Isn't it an `HttpResponseMessage` since `HttpResponse` expects an `System.IO.TextWriter`? `var response = new HttpResponseMessage(HttpStatusCode.NotFound);` – Tiago César Oliveira Mar 13 '14 at 13:59
  • So, [my question is similar](https://stackoverflow.com/questions/50274989/) - I like how my generated AutoRest client deserializes my main entities when dealing with the 200 scenarios.. I wish I didn't have to manually parse the 400 scenarios – bkwdesign May 10 '18 at 14:44
  • In .NET Core there is e.g. BadRequestObjectResult and NotFoundObjectResult which allow you to return results with objects / messages if that is the only customisation required (and you are returning one of the types that have ObjectResult varieties) – Claire Furney May 21 '21 at 15:33
  • https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling – carloswm85 Feb 01 '23 at 17:27
206

ASP.NET Web API 2 really simplified it. For example, the following code:

public HttpResponseMessage GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        HttpError err = new HttpError(message);
        return Request.CreateResponse(HttpStatusCode.NotFound, err);
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.OK, item);
    }
}

returns the following content to the browser when the item is not found:

HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51

{
  "Message": "Product with id = 12 not found"
}

Suggestion: Don't throw HTTP Error 500 unless there is a catastrophic error (for example, WCF Fault Exception). Pick an appropriate HTTP status code that represents the state of your data. (See the apigee link below.)

Links:

BrianS
  • 13,284
  • 15
  • 62
  • 125
Manish Jain
  • 9,569
  • 5
  • 39
  • 44
  • 4
    I would go one step further and throw a ResourceNotFoundException from the DAL/Repo which I check in the Web Api 2.2 ExceptionHandler for the Type ResourceNotFoundException and then I return "Product with id xxx not found". That way its generically anchored in the architecture instead of each action. – Pascal Jul 07 '15 at 21:27
  • 1
    Is there any specific use for the `return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);` What is the difference between `CreateResponse` and `CreateErrorResponse` – Zapnologica Aug 06 '15 at 07:34
  • 14
    According to, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html, a client error is a 400 level code and a server error is a 500 level code. So a 500 error code might be very appropriate in many cases for a Web API, not just "catastrophic" errors. – Jess Aug 26 '15 at 15:41
  • 2
    You need `using System.Net.Http;` for the `CreateResponse()` extension method to show up. – Adam Szabo Oct 14 '15 at 13:37
  • 1
    What I don't like about using Request.CreateResponse() is that it returns unnecessary Microsoft-specific serialization info like "My error here". For situations when 400 status is appropriate I found that ApiController.BadRequest(string message) returns a better "My error here" string. But I cannot find its equivalent for returning 500 status with a simple message. – vkelman Feb 11 '16 at 16:47
  • Actually, similar result for 500 might be achieved (see http://goo.gl/0qfD5D) by using ResponseMessage(new HttpResponseMessage { Content = new StringContent("My error message", System.Text.Encoding.UTF8, "application/json"), StatusCode = HttpStatusCode.InternalServerError }); – vkelman Feb 11 '16 at 17:48
  • Then essentially return type of every method of my web API will be `HttpResponseMessage`. Is that ok? Whether I'm returning a string, json or a custom object. Whatever be my final return thing I simply assign it to item property. Is that a correct understanding from my end? – RBT Dec 13 '16 at 07:01
  • How can I return a json result? Something like this:`return Request.CreateResponse(HttpStatusCode.OK, Json(bonds));` like `JsonResult` – Shalom Dahan Nov 16 '17 at 16:29
  • I agree. I think we need to stop using Exceptions unless something, well, exceptional, has actually happened. Just because you want to return an HTTP 500 to the client doesn't mean you need to use an exception for this, including the work that the run-time has to do to wrap, then unwrap that exception. Just return the 500. If I see something go through my applications general exception handler, I want that to be because something that I did not expect to happen, happened, NOT because the user tried to do something they weren't supposed to do (like call the API with no name). – Jamie Nordmeyer Dec 31 '17 at 01:38
  • While I agree that exceptions should not be thrown lightly, the current solution abstracts away the service return type from specifif repository item type to "it can return anything". I think exception would be the lesser evil. – Imre Pühvel Aug 13 '18 at 08:49
  • [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status405MethodNotAllowed)] return NotFound(); return BadRequest() . Task allows you to use the controller to detect error conditions and return NotFound or BadRequest to the client. you can do all error condition logic in the controller – Golden Lion Jun 24 '20 at 22:21
  • I don't like this. 404 means page not found or url resource wasn't found. The API endpoint WAS found so it's a 200. The fact that there was no data shouldn't be a 404. There should be an empty data set and perhaps a message for the client. I'd use a 200. It's a web success but no data. If it was a file it could be a 404. – Norbert Norbertson Dec 12 '22 at 12:22
85

It looks like you're having more trouble with Validation than errors/exceptions so I'll say a bit about both.

Validation

Controller actions should generally take Input Models where the validation is declared directly on the model.

public class Customer
{ 
    [Require]
    public string Name { get; set; }
}

Then you can use an ActionFilter that automatically sends validation messages back to the client.

public class ValidationActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var modelState = actionContext.ModelState;

        if (!modelState.IsValid) {
            actionContext.Response = actionContext.Request
                 .CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
        }
    }
} 

For more information about this check out http://ben.onfabrik.com/posts/automatic-modelstate-validation-in-aspnet-mvc

Error handling

It's best to return a message back to the client that represents the exception that happened (with relevant status code).

Out of the box you have to use Request.CreateErrorResponse(HttpStatusCode, message) if you want to specify a message. However, this ties the code to the Request object, which you shouldn't need to do.

I usually create my own type of "safe" exception that I expect the client would know how to handle and wrap all others with a generic 500 error.

Using an action filter to handle the exceptions would look like this:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        var exception = context.Exception as ApiException;
        if (exception != null) {
            context.Response = context.Request.CreateErrorResponse(exception.StatusCode, exception.Message);
        }
    }
}

Then you can register it globally.

GlobalConfiguration.Configuration.Filters.Add(new ApiExceptionFilterAttribute());

This is my custom exception type.

using System;
using System.Net;

namespace WebApi
{
    public class ApiException : Exception
    {
        private readonly HttpStatusCode statusCode;

        public ApiException (HttpStatusCode statusCode, string message, Exception ex)
            : base(message, ex)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode, string message)
            : base(message)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode)
        {
            this.statusCode = statusCode;
        }

        public HttpStatusCode StatusCode
        {
            get { return this.statusCode; }
        }
    }
}

An example exception that my API can throw.

public class NotAuthenticatedException : ApiException
{
    public NotAuthenticatedException()
        : base(HttpStatusCode.Forbidden)
    {
    }
}
Christian Casutt
  • 2,334
  • 4
  • 29
  • 38
Daniel Little
  • 16,975
  • 12
  • 69
  • 93
  • I have an issue related with the error handling answer at the ApiExceptionFilterAttribute class definition. In the OnException method, the exception.StatusCode is not valid since exception is a WebException. What can I do in this case? – razp26 Oct 01 '15 at 21:24
  • 1
    @razp26 if you're referring to the like `var exception = context.Exception as WebException;` that was a typo, it should have been `ApiException` – Daniel Little Oct 02 '15 at 23:39
  • 2
    Can you please add an example of how the ApiExceptionFilterAttribute class would be used? – razp26 Oct 05 '15 at 15:18
40

You can throw a HttpResponseException

HttpResponseMessage response = 
    this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "your message");
throw new HttpResponseException(response);
tartakynov
  • 2,768
  • 3
  • 26
  • 23
  • These classes/methods don't seem to be available in .net 5. Maybe BadHttpRequestException replaces these. – steve Jun 05 '21 at 08:35
33

If you are using ASP.NET Web API 2, the easiest way is to use the ApiController Short-Method. This will result in a BadRequestResult.

return BadRequest("message");
Fabian von Ellerts
  • 4,763
  • 40
  • 35
  • 3
    Strictly for model validation errors I use the overload of BadRequest() that accepts the ModelState object: `return BadRequest(ModelState);` – timmi4sa Sep 09 '19 at 21:01
25

For Web API 2 my methods consistently return IHttpActionResult so I use...

public IHttpActionResult Save(MyEntity entity)
{
    ....
    if (...errors....)
        return ResponseMessage(
            Request.CreateResponse(
                HttpStatusCode.BadRequest, 
                validationErrors));

    // otherwise success
    return Ok(returnData);
}
Mick
  • 6,527
  • 4
  • 52
  • 67
5

You can use custom ActionFilter in Web Api to validate model:

public class DRFValidationFilters : ActionFilterAttribute
{

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = actionContext.Request
                .CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);

            //BadRequest(actionContext.ModelState);
        }
    }

    public override Task OnActionExecutingAsync(HttpActionContext actionContext,
        CancellationToken cancellationToken)
    {

        return Task.Factory.StartNew(() =>
        {
            if (!actionContext.ModelState.IsValid)
            {
                actionContext.Response = actionContext.Request
                    .CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        });
    }

    public class AspirantModel
    {
        public int AspirantId { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string AspirantType { get; set; }
        [RegularExpression(@"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$",
            ErrorMessage = "Not a valid Phone number")]
        public string MobileNumber { get; set; }
        public int StateId { get; set; }
        public int CityId { get; set; }
        public int CenterId { get; set; }


        [HttpPost]
        [Route("AspirantCreate")]
        [DRFValidationFilters]
        public IHttpActionResult Create(AspirantModel aspirant)
        {
            if (aspirant != null)
            {

            }
            else
            {
                return Conflict();
            }

            return Ok();
        }
    }
}

Register CustomAttribute class in webApiConfig.cs config.Filters.Add(new DRFValidationFilters());

Metro Smurf
  • 37,266
  • 20
  • 108
  • 140
4

Building up upon Manish Jain's answer (which is meant for Web API 2 which simplifies things):

1) Use validation structures to response as many validation errors as possible. These structures can also be used to response to requests coming from forms.

public class FieldError
{
    public String FieldName { get; set; }
    public String FieldMessage { get; set; }
}

// a result will be able to inform API client about some general error/information and details information (related to invalid parameter values etc.)
public class ValidationResult<T>
{
    public bool IsError { get; set; }

    /// <summary>
    /// validation message. It is used as a success message if IsError is false, otherwise it is an error message
    /// </summary>
    public string Message { get; set; } = string.Empty;

    public List<FieldError> FieldErrors { get; set; } = new List<FieldError>();

    public T Payload { get; set; }

    public void AddFieldError(string fieldName, string fieldMessage)
    {
        if (string.IsNullOrWhiteSpace(fieldName))
            throw new ArgumentException("Empty field name");

        if (string.IsNullOrWhiteSpace(fieldMessage))
            throw new ArgumentException("Empty field message");

        // appending error to existing one, if field already contains a message
        var existingFieldError = FieldErrors.FirstOrDefault(e => e.FieldName.Equals(fieldName));
        if (existingFieldError == null)
            FieldErrors.Add(new FieldError {FieldName = fieldName, FieldMessage = fieldMessage});
        else
            existingFieldError.FieldMessage = $"{existingFieldError.FieldMessage}. {fieldMessage}";

        IsError = true;
    }

    public void AddEmptyFieldError(string fieldName, string contextInfo = null)
    {
        AddFieldError(fieldName, $"No value provided for field. Context info: {contextInfo}");
    }
}

public class ValidationResult : ValidationResult<object>
{

}

2) Service layer will return ValidationResults, regardless of operation being successful or not. E.g:

    public ValidationResult DoSomeAction(RequestFilters filters)
    {
        var ret = new ValidationResult();

        if (filters.SomeProp1 == null) ret.AddEmptyFieldError(nameof(filters.SomeProp1));
        if (filters.SomeOtherProp2 == null) ret.AddFieldError(nameof(filters.SomeOtherProp2 ), $"Failed to parse {filters.SomeOtherProp2} into integer list");

        if (filters.MinProp == null) ret.AddEmptyFieldError(nameof(filters.MinProp));
        if (filters.MaxProp == null) ret.AddEmptyFieldError(nameof(filters.MaxProp));


        // validation affecting multiple input parameters
        if (filters.MinProp > filters.MaxProp)
        {
            ret.AddFieldError(nameof(filters.MinProp, "Min prop cannot be greater than max prop"));
            ret.AddFieldError(nameof(filters.MaxProp, "Check"));
        }

        // also specify a global error message, if we have at least one error
        if (ret.IsError)
        {
            ret.Message = "Failed to perform DoSomeAction";
            return ret;
        }

        ret.Message = "Successfully performed DoSomeAction";
        return ret;
    }

3) API Controller will construct the response based on service function result

One option is to put virtually all parameters as optional and perform custom validation which return a more meaningful response. Also, I am taking care not to allow any exception to go beyond the service boundary.

    [Route("DoSomeAction")]
    [HttpPost]
    public HttpResponseMessage DoSomeAction(int? someProp1 = null, string someOtherProp2 = null, int? minProp = null, int? maxProp = null)
    {
        try
        {
            var filters = new RequestFilters 
            {
                SomeProp1 = someProp1 ,
                SomeOtherProp2 = someOtherProp2.TrySplitIntegerList() ,
                MinProp = minProp, 
                MaxProp = maxProp
            };

            var result = theService.DoSomeAction(filters);
            return !result.IsError ? Request.CreateResponse(HttpStatusCode.OK, result) : Request.CreateResponse(HttpStatusCode.BadRequest, result);
        }
        catch (Exception exc)
        {
            Logger.Log(LogLevel.Error, exc, "Failed to DoSomeAction");
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new HttpError("Failed to DoSomeAction - internal error"));
        }
    }
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
3

Use the built in "InternalServerError" method (available in ApiController):

return InternalServerError();
//or...
return InternalServerError(new YourException("your message"));
Rusty
  • 109
  • 1
  • 9
3

Welcome to 2022! Now we have other answers in .NET (since ASP.NET Core 2.1). Look at this article: Using the ProblemDetails Class in ASP.NET Core Web API, where the author explains the following best practices:

  1. How to implement standard IETF RFC 7807, which defines a "problem detail" as a way to carry machine-readable details of errors in an HTTP response to avoid the need to define new error response formats for HTTP APIs.
  2. How model validations use the ProblemDetails class to populate a list of validation errors - the direct answer to the question for a general rule, whether to break processing after the first error.

As a teaser, this is how the JSON output looks if we use ProductDetails and multiple errors:

enter image description here

1

Some of these answers seem to be relics of the past. I've found the solution below to be simple and work well. This is in .NET 6 for a Web API derived from ControllerBase.

Rather than throwing exceptions, you can directly return the various HTTP response codes as objects, along with an exact error message:

using Microsoft.AspNetCore.Mvc;

[ApiController]
public class MyWebApiController : ControllerBase
{
    [HttpPost]
    public ActionResult<int> Process(Customer customer)
    {
        if (string.IsNullOrEmpty(customer.Name))
            return BadRequest("Customer Name cannot be empty");

        if (!Customers.Find(customer))
            return NotFound("Customer does not have an account");

        // After validating inputs, core logic goes here...

        return Ok(customer.ID);  // or simply "return Ok()" if not returning data
    }
}

See a list of error codes available here.

As for when to return the errors (OP's question), it depends on the requirement. Returning errors as they happen means you avoid the overhead of additional processing, but then the client has to make repeated calls to get all the errors. Consider the server viewpoint also, as it may cause undesirable program behavior to continue server-side processing when an error has occurred.

Tawab Wakil
  • 1,737
  • 18
  • 33
0

Just to update on the current state of ASP.NET WebAPI. The interface is now called IActionResult and implementation hasn't changed much:

[JsonObject(IsReference = true)]
public class DuplicateEntityException : IActionResult
{        
    public DuplicateEntityException(object duplicateEntity, object entityId)
    {
        this.EntityType = duplicateEntity.GetType().Name;
        this.EntityId = entityId;
    }

    /// <summary>
    ///     Id of the duplicate (new) entity
    /// </summary>
    public object EntityId { get; set; }

    /// <summary>
    ///     Type of the duplicate (new) entity
    /// </summary>
    public string EntityType { get; set; }

    public Task ExecuteResultAsync(ActionContext context)
    {
        var message = new StringContent($"{this.EntityType ?? "Entity"} with id {this.EntityId ?? "(no id)"} already exist in the database");

        var response = new HttpResponseMessage(HttpStatusCode.Ambiguous) { Content = message };

        return Task.FromResult(response);
    }

    #endregion
}
Thomas Hagström
  • 4,371
  • 1
  • 21
  • 27
  • This looks interesting, but where specifically in the project is this code placed? I am doing my web api 2 project in vb.net. – Off The Gold Sep 20 '16 at 19:25
  • It's just a model for returning the error and can reside anywhere. You would return a new instance of the above class in your Controller. But to be honest I try to use the built in classes whenever possible: this.Ok(), CreatedAtRoute(), NotFound(). The signature of the method would be IHttpActionResult. Don't know if they changed all this with NetCore – Thomas Hagström Sep 22 '16 at 09:28
0

Try this

[HttpPost]
public async Task<ActionResult<User>> PostUser(int UserTypeId, User user)
{
  if (somethingFails)
  {
    // Return the error message like this.
    return new BadRequestObjectResult(new
    {
      message = "Something is not working here"
    });
  }

  return ok();
}
CinCout
  • 9,486
  • 12
  • 49
  • 67
Zablon
  • 117
  • 1
  • 4
  • This is not for WebAPI, is for MVC... – benjamingranados Feb 14 '22 at 17:37
  • 1
    This is directly copied from a web api repo @benjamingranados – Zablon Feb 16 '22 at 22:19
  • If your source is https://www.likecs.com/ask-77378.html, is still not for WebApi, is just a "Try this" response on a forum. Maybe you can share the source link. – benjamingranados Feb 16 '22 at 22:42
  • 1
    I'm giving you a upvote here, this worked fine and was what I was looking for on my webapi C# 6 implementation: return BadRequest(new { message = "The service is not implemented as of yet." }); – Terje Solem Oct 18 '22 at 08:15
  • Seems like this link supports @Zablon https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-7.0#asynchronous-action-1 – paulguy Dec 12 '22 at 01:59
-2

For those errors where modelstate.isvalid is false, I generally send the error as it is thrown by the code. Its easy to understand for the developer who is consuming my service. I generally send the result using below code.

     if(!ModelState.IsValid) {
                List<string> errorlist=new List<string>();
                foreach (var value in ModelState.Values)
                {
                    foreach(var error in value.Errors)
                    errorlist.Add( error.Exception.ToString());
                    //errorlist.Add(value.Errors);
                }
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest,errorlist);}

This sends the error to the client in below format which is basically a list of errors:

    [  
    "Newtonsoft.Json.JsonReaderException: **Could not convert string to integer: abc. Path 'Country',** line 6, position 16.\r\n   
at Newtonsoft.Json.JsonReader.ReadAsInt32Internal()\r\n   
at Newtonsoft.Json.JsonTextReader.ReadAsInt32()\r\n   
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter, Boolean inArray)\r\n   
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)",

       "Newtonsoft.Json.JsonReaderException: **Could not convert string to integer: ab. Path 'State'**, line 7, position 13.\r\n   
at Newtonsoft.Json.JsonReader.ReadAsInt32Internal()\r\n   
at Newtonsoft.Json.JsonTextReader.ReadAsInt32()\r\n   
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter, Boolean inArray)\r\n   
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)"
    ]
Sylvain
  • 19,099
  • 23
  • 96
  • 145
Ashish Sahu
  • 185
  • 1
  • 6
  • I wouldn't recommend sending back this level of detail in the exception if this was an external API (i.e. exposed to the public internet). You should do some more work in the filter and send back a JSON object (or XML if that's the chosen format) detailing the error rather than just a ToString of exception. – Sudhanshu Mishra Aug 31 '16 at 00:54
  • Correct dont sent this exception for external API. But you can use it to debug issues in internal API and during testing. – Ashish Sahu Sep 20 '17 at 06:36