7

The template WebApi controller in MVC6/WebApi implements an action that returns a collection for the Get method like this:

[HttpGet]
public IEnumerable<MyEntity> Get()
{
    //my code to return entities
}

Assuming that my code to return the result throws an exception, how would I return an error message to the consumer?

As far as I noticed an exception would result in HTTP 500. This is fine but I would like to give the caller a message telling him what went wrong. Because of the signature of the template action I can not catch the exception and return some Http*** or ObjectResult instance.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
NicolasR
  • 2,222
  • 3
  • 23
  • 38
  • Have a look at the answers to [this](http://stackoverflow.com/q/31054012/5233410) question – Nkosi Mar 09 '16 at 12:46

5 Answers5

12

You will need to add some piece of code yourself that will handle errors and return a message.

One option is to use an exception filter and add it either globally or on selected controllers, although this approach would only cover exceptions coming from the controller action methods. For example the following filter will return a json object only when the request accept was application/json (Otherwise it would let the exception pass through which for example could be handled by the global error page):

public class CustomJSONExceptionFilter : ExceptionFilterAttribute
{    
    public override void OnException(ExceptionContext context)
    {
        if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
        {
            var jsonResult = new JsonResult(new { error = context.Exception.Message });
            jsonResult.StatusCode = Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError;
            context.Result = jsonResult;
        }
    }
}

services.AddMvc(opts => 
{
    //Here it is being added globally. 
    //Could be used as attribute on selected controllers instead
    opts.Filters.Add(new CustomJSONExceptionFilter());
});

Another option is You can change the signature to make providing a response more flexible. Then you can handle the error like one normally would and then return a user friendly error message.

public IActionResult Get() {
    try {
        IEnumerable<MyEntity> result;
        //...result populated
       return new HttpOkObjectResult(result);
    } catch (Exception ex) {
        //You should handle the error
        HandleError(ex);//the is not an actual method. Create your own.
        //You could then create your own error so as not to leak
        //internal information.
        var error = new 
            { 
                 message = "Enter you user friendly error message",
                 status = Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError
            };
        Context.Response.StatusCode = error.status;            
        return new ObjectResult(error);
    }
}
Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 1
    Together with the link that you provided that makes three possible ways to do it. I will see what fits best. I will accept your answer! – NicolasR Mar 09 '16 at 15:20
2

Change your method to look like

[HttpGet]
[ResponseType(typeof(IEnumerable<MyEntity>))]
public IHttpActionResult Get()
{
    //when ok
    return Ok(response); // response is your IEnumerable<MyEntity>

    //when error
    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.WhateverStatusCodeSuitable)
            {
                ReasonPhrase = "your message"
            });

}
ChrisS
  • 376
  • 1
  • 11
  • 3
    Looks nice but as explained in this post http://stackoverflow.com/questions/31054012/asp-net-5-mvc-6-equivalent-of-httpexception?lq=1 HttpResponseException is not available anymore. – NicolasR Mar 09 '16 at 12:54
2

Migth be little bit late to anserw but now the best way to get a custom error with a custom code seems to be using StatusCode method.

` [HttpGet("{id}")]
[ProducesResponseType(typeof(IEnumerable<string>), 200)]
[ProducesResponseType(typeof(void), 404)]
public IActionResult Get(int id)
{
    Product product = null;
    if (!this.productRepository.TryGet(id, out product))
    {
        return StatsusCode(500, "NotFound");
    }

    return Ok(product);
}`
Assassin
  • 1,296
  • 2
  • 14
  • 17
1

Just like you would to keep any program running.

try {
    ...
} catch (Exception e) {
    return errorMessageView;
}

Alternatively you can use the HandleErrorAttribute.

will
  • 1,491
  • 1
  • 19
  • 28
  • Thanks, but I think I wasn't clear enough. I am writing an API so I don't want to return a view. The client should check for Http OK (200) or 500 (internal error). In case the client got 500 he should be able to display an error message. How do I get this error message from the exception handler to the client? Searching for HandleErrorAttribute I found HttpResponseException but this seems not to be supported in MVC 6. – NicolasR Mar 09 '16 at 01:49
  • can you return JSON `{'result': 'error', 'status', 500}`? – will Mar 09 '16 at 02:04
  • Only when I change the return statement but that feels unnatural because the controller template always creates Get actions with returning collections. From what others suggested it looks like the best way is to let the exception fall through and implement a filter or error middleware. – NicolasR Mar 09 '16 at 12:59
0

You can mess around with exceptions, but I get the impression we're not supposed to think like that any more. This seems to be the zen of MVC6:

    [HttpGet("{id}")]
    [ProducesResponseType(typeof(IEnumerable<string>), 200)]
    [ProducesResponseType(typeof(void), 404)]
    public IActionResult Get(int id)
    {
        Product product = null;
        if (!this.productRepository.TryGet(id, out product))
        {
            return NotFound();
        }

        return Ok(product);
    }
Quark Soup
  • 4,272
  • 3
  • 42
  • 74
  • If it were only about found or not found, then ok. But what about all the actual exceptions that happen in realistic scenarios like broken database, unavailable nested services and, not to forget, bugs introduced by programmers. I agree that IActionResult would give me the freedom to return whatever is needed but that would mean that I had to catch exceptions in every controller method just to convert it to something like "An error occurred, please consult..." (as the minimum). – NicolasR Jun 09 '17 at 23:28
  • Whether you like it or not, all those exceptions are turned into HTTP results of one form or another. This, IMO, is just capturing that paradigm rather than giving the illusion that some exception is actually passed back to the client. – Quark Soup Jun 10 '17 at 11:13
  • I agree with you about the paradigm and I use code like you posted in most of my methods. On the other side, have a look at the second code snippet in the answer of Nkosi. I really don't want to have that kind of code in each of my controllers. My problem is that the default exception handler only returns code 500 to the client without any error text. That is why I like the CustomExceptionFilter solution. – NicolasR Jun 11 '17 at 18:46