1

I'm using EmbedIO with the Web API module.

I'd like to have an exception handler that will catch all the unhandled exceptions and return a suited HTTP error code according to the exception type. However, it's not clear if this can be achieved.

The class WebModuleBase exposes a property called OnUnhandledException that can be set to an ExceptionHandlerCallback, but when the callback is invoked, the response's status code has already been set to HttpStatusCode.InternalServerError, as stated in the code comments.

This is not convenient since I want to set the response code myself.

WebModuleBase exposes a different property called OnHttpException that can be set to a HttpExceptionHandlerCallback. This can be set to HttpExceptionHandler.DataResponse(ResponseSerializer.Json) which partially solves the issue.

The main concern now is that the application exceptions must be converted to HttpException in the controllers.

I'd like to throw custom exceptions from the domain code, get them in an exception handler and just return a HTTPException in there, according to the initial exception.

Basically something similar to Exception Filters in ASP.NET Web API.

Here's the code to setup the web server:

var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
    .WithController<MyController>();
webApiModule.OnUnhandledException = ExceptionHandler.DataResponseForException();
webApiModule.OnHttpException = ExceptionHandler.DataResponseForHttpException();

WebServerEmbedded = new EmbedIO.WebServer(
    opt => opt
        .WithUrlPrefix(url)
        .WithMode(HttpListenerMode.EmbedIO))
        .WithModule(null, webApiModule);

These are the delegates used for exception handlers:

internal static class ExceptionHandler
{
    public static ExceptionHandlerCallback DataResponseForException()
    {
        return (context, exception) => ResponseSerializer.Json(context, exception.Message);
    }

    public static HttpExceptionHandlerCallback DataResponseForHttpException()
    {
        return (context, httpException) => ResponseSerializer.Json(context, httpException.Message);
    }
}

Thanks.

Paul
  • 1,224
  • 2
  • 14
  • 31
  • Do you want to use custom Exception, let's say InvalidOperationException, and throw it and transform this to an HTTPException? – Geo Perez Jan 05 '20 at 16:43
  • Yes, exactly. For instance let's say that my repository layer is throwing a custom exception called EntityNotFoundException. I'd like to be able to catch this in the web project in an exception handler and throw a HttpNotFound based on it. – Paul Jan 05 '20 at 16:50
  • Can you post this issue at https://github.com/unosquare/embedio/issues/new?template=Bug_report.md – Geo Perez Jan 05 '20 at 17:50
  • I found a workaround, I will publish this fix soon. – Geo Perez Jan 05 '20 at 17:52
  • You can check the PR: https://github.com/unosquare/embedio/pull/427 – Geo Perez Jan 05 '20 at 18:02
  • Thanks, I checked the PR, it seems to solve my issue. Just to make sure I understood correctly, you're actually catching and re-throwing the HttpException thrown from the OnUnhandledException exception handler, right? Will the OnHttpException exception handler be subsequently invoked on that exception? – Paul Jan 05 '20 at 19:30
  • It should, but I didn't try it. – Geo Perez Jan 05 '20 at 22:10
  • It is not passing to the OnHttpException from the same module, only to the parent...Check my PR with the sample code. – Geo Perez Jan 05 '20 at 22:14

1 Answers1

3

Exceptions, as well as HTTP exceptions, are handled by EmbedIO at both module and server level (each nested module group introduces a further level, but that's beyond the point).

The catch clause for HTTP exceptions always comes before the "general-purpose" catch clause, for the obvious reason that HTTP exceptions are exceptions themselves and need to be sorted out. Therefore, if an exception handler throws a HTTP exception, the latter must be handled at an outer level.

In opther words, you can write a module-level exception handler that throws a HTTP exception, then use a server-level HTTP exception handler to generate the appropriate response.

var webApiModule = new WebApiModule("/api", ResponseSerializer.Json)
    .WithController<MyController>()
    .HandleUnhandledException(ExceptionHandler.DataResponseForException));

WebServerEmbedded = new EmbedIO.WebServer(
    opt => opt
        .WithUrlPrefix(url)
        .WithMode(HttpListenerMode.EmbedIO))
    .WithModule(webApiModule)
    .HandleHttpException(ExceptionHandler.DataResponseForHttpException);
internal static class ExceptionHandler
{
    public static Task DataResponseForException(IHttpContext context, Exception exception)
    {
        // Replace ANY_VALID_STATUS CODE with, well, any valid status code.
        // Of course you can use different status codes according to, for example,
        // the type of exception.
        throw new HttpException(ANY_VALID_STATUS_CODE, exception.Message);
    }

    public static Task DataResponseForHttpException(IHttpContext context, IHttpException httpException)
    {
        context.Response.StatusCode = (int)HttpStatusCode.OK;
        return ResponseSerializer.Json(context, httpException.Message);
    }
}

EDIT: There's an even simpler way, if you need it for custom exceptions: just have your exceptions implement IHttpException.

Here you can see how IHttpException methods are used by the HTTP exception handling code.

Here is an example of probably the most obscure method, PrepareResponse.

EDIT: I added setting the status code in DataResponseForHttpException.

rdeago
  • 136
  • 2
  • 8
  • Thanks for your answer. I tried the first approach you suggested but the code is always 500. Please see the beginning of the question description: "The class WebModuleBase exposes a property called OnUnhandledException that can be set to an ExceptionHandlerCallback, but when the callback is invoked, the response's status code has already been set to HttpStatusCode.InternalServerError, as stated in the code comments." – Paul Jan 11 '20 at 19:03
  • As for the second suggestion, I cannot have my Exception classes that have nothing to do with HTTP implement IHttpException. Or did I misunderstand your suggestion? – Paul Jan 11 '20 at 19:03
  • @Paul the response's status code has already been set, but it can be set again by your exceptiopn handler, because headers have not yet been sent to the client when it is called. – rdeago Jan 12 '20 at 05:28
  • Of course you can have your own exception classes, and there's no obligation for them to implement `IHttpException`. I was just suggestiing that, if the exceptions you care about are all your own (i.e. part of your application) having them implement `IHttpException` is a quick way to give each of them its own response code, along with their own message, and even custom headers if needed. – rdeago Jan 12 '20 at 05:33
  • Besides, if your response code would depend upon the type of the exception, having each response code set directly by the exception - instead of in a `switch` statement (or a series of `catch` clauses) in an exception handler - would make for cleaner code. – rdeago Jan 12 '20 at 05:37
  • "the response's status code has already been set, but it can be set again by your exceptiopn handler" - Could you please update your answer with the code that does this, so I can mark it as the correct answer? – Paul Jan 14 '20 at 09:21
  • Thanks. I think you guys should watch the [embedio] tag here on SO, since it's a bit inappropriate to report issues in your repos in order to ask questions. As I noticed this may lead to [unnecessary pull requests](https://github.com/unosquare/embedio/pull/431). – Paul Jan 16 '20 at 18:03