4

I would like to know how to better solve this problem.

The app has mvc web and web api registered. I have a separate mvc web controller (called Error) responsible for rendering error pages. My web.config looks like:

<system.webServer>  
    <httpErrors errorMode="Custom" existingResponse="Replace">
      <remove statusCode="404" />
      <remove statusCode="500" />
      <error statusCode="404" responseMode="ExecuteURL" path="/error/404" />
      <error statusCode="500" responseMode="ExecuteURL" path="/error/500/" />
    </httpErrors>
</system.webServer>

It works for mvc web pages but for web api now when I return NotFound() I get html error pages in return. I thought to fix this by using location attribute:

<location path="api">
    <system.webServer>
      <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" />
        <remove statusCode="500" />
      </httpErrors>
    </system.webServer>
</location>

So, now it doesn't return the html but just an error string from asp.net.

But what if I need to return some object from web api and just set error header in the action for example or just the error header without data?

Thanks

Andrey Borisko
  • 4,511
  • 2
  • 22
  • 31
  • I would take a look at [this question](http://stackoverflow.com/questions/10732644/best-practice-to-return-errors-in-asp-net-web-api) – balexandre Sep 17 '15 at 23:39

2 Answers2

1

I'm not certain this is what you are trying to do precisely, but if you just want to send and object (json) on error, you can try this. This is certainly not the best way out though!

The idea is to save the static object as text in a file, and serve the file content on error.

Set the custom error for path api in web.config

<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404" />
  <remove statusCode="500" />
  <error statusCode="404" responseMode="File" path="templates\404.json" />
  <error statusCode="500" responseMode="File" path="templates\500.json" />
</httpErrors>

And, add the file 500.json in path \templates with content

{
    "Code": 500,
    "Message":  "Something went wrong, please try again later" 
}

Now on internal server error (http status code 500), this is returned by the server

500 Error in API

Note: Check permission to the directory, and do a iisreset.


Option 2 - Exception filter

Create a custom API exception filter attribute which returns a json on exception. The message can be customized per controller/action/globally.

public class ApiExceptionAttribute : ExceptionFilterAttribute 
{
    string _message = null;

    public ApiExceptionAttribute(string exceptionMessage = null) //to add custom message
    {
        _message = exceptionMessage;
    }

    public override void OnException(HttpActionExecutedContext context)
    {
        var message = new { 
            ExceptionType = "Custom", //or some other detail
            Message = _message == null ? "Something went wrong, please try later" : _message 
        };
        context.Response = context.Request.CreateResponse(HttpStatusCode.SeeOther, message, "application/json");
    }
}

And use it in API controller (optionally with a custom message)

[ApiExceptionAttribute("This end point is to test error!")]
public class TestController : ApiController
{
    public IEnumerable<string> Get()
    {
        //actual code here
        throw new Exception("Back-end exception");
    }
}
Arghya C
  • 9,805
  • 2
  • 47
  • 66
0

I've found one solution that works well, but instead of making any changes to Web API side, I did the changes to MVC Web.

So after reading this I realized that Web API exceptions will not be handled in global.asax and for that you need to register IExceptionLoggers or IExceptionHandler. So what I did to resolve the problem is:

  1. Removed the settings mentioned in the question from web.config and added empty <customErrors mode="On"></customErrors> there.
  2. Add something similar to Global.asax (based on the article):

    protected void Application_Error()
    {
        if (Context.IsCustomErrorEnabled)
        {
            ShowWebErrorPage(Server.GetLastError());
        }
    }
    
    private void ShowWebErrorPage(Exception exception)
    {
        var httpException = exception as HttpException ?? new HttpException(500, "Internal Server Error", exception);
    
        Response.Clear();
        var routeData = new RouteData();
        // these depend on your Error controller's logic and routing settings
        routeData.Values.Add("controller", "Error");
        routeData.Values.Add("action", "Index");
        routeData.Values.Add("statusCode", httpException.GetHttpCode());
        Server.ClearError();
    
        IController controller = new Controllers.ErrorController();
        controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
    

So this piece of code is responsible only for Mvc Web pages. ErrorController decides what View needs to be rendered.

Web Api's default behavior of that wasn't changed, so it works as before and doesn't render any of those erro html in response. They are separated now. Within the actions I can manually do:

return NotFound();

where I see empty response body or control it with something like this

return Request.CreateResponse(HttpStatusCode.BadRequest, error);
Andrey Borisko
  • 4,511
  • 2
  • 22
  • 31
  • I had to add Response.End(); at the end of the ShowWebErroPage method and Response.TrySkipIisCustomErrors = true; in the controller action method to avoid blank page that was comming from IIS. – Vojta Jemelka Feb 07 '17 at 10:35