12

I've written simple server using Owin Self-hosting and WebApi:

namespace OwinSelfHostingTest
{
    using System.Threading;
    using System.Web.Http;
    using Microsoft.Owin.Hosting;
    using Owin;

    public class Startup
    {
        public void Configuration(IAppBuilder builder)
        {
            var config = new HttpConfiguration();

            config.Routes.MapHttpRoute(
                "Default",
                "{controller}/{id}",
                new { id = RouteParameter.Optional }
                );

            builder.UseWebApi(config);
        }
    }

    public class Server
    {
        private ManualResetEvent resetEvent = new ManualResetEvent(false);
        private Thread thread;

        private const string ADDRESS = "http://localhost:9000/";

        public void Start()
        {
            this.thread = new Thread(() =>
                {
                    using (var host = WebApp.Start<Startup>(ADDRESS))
                    {
                        resetEvent.WaitOne(Timeout.Infinite, true);
                    }
                });
            thread.Start();
        }

        public void Stop()
        {
            resetEvent.Set();
        }
    }

}

When there is exception in controller, then Owin returns XML response like this:

<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>Attempted to divide by zero.</ExceptionMessage>
    <ExceptionType>System.DivideByZeroException</ExceptionType>
    <StackTrace> 
        ...
    </StackTrace>
</Error>

But i want different output - so how can i override this?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
rufanov
  • 3,266
  • 1
  • 23
  • 41

2 Answers2

10

You do so by creating an OWIN MiddleWare and hooking it into the pipeline:

public class CustomExceptionMiddleware : OwinMiddleware
{
   public CustomExceptionMiddleware(OwinMiddleware next) : base(next)
   {}

   public override async Task Invoke(IOwinContext context)
   {
      try
      {
          await Next.Invoke(context);
      }
      catch(Exception ex)
      {
          // Custom stuff here
      }
   }
 }

And hook it on startup:

public class Startup
{
    public void Configuration(IAppBuilder builder)
    {
        var config = new HttpConfiguration();

        config.Routes.MapHttpRoute(
            "Default",
            "{controller}/{id}",
            new { id = RouteParameter.Optional }
            );

        builder.Use<CustomExceptionMiddleware>().UseWebApi(config);
    }
}

That way any unhandled exception will be caught by your middleware and allow you to customize the output result.

An important thing to note: if the thing being hosted has an exception handling logic of it's own, as WebAPI does, exceptions will not propagate. This handler is meant for any exception which goes by unhandeled by the underlying service being hosted.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 7
    Btw that doesn't work. Upon calling Next.Invoke() the catch block is never triggered. Stumbled upon this on my searching would be nice to know how to do this. The exception is swallowed doesn't appear the context or the dictionary. – Dr Schizo Dec 16 '15 at 17:39
  • @DrSchizo Well, if your exception is handeled by WebAPI, of course you won't see it hitting here, as it's already handeled and contained inside the said framework. – Yuval Itzchakov Sep 21 '16 at 04:48
1

When you await a async method, an exception won't be thrown into the caller's context, but will be available by inspecting the task returned to the caller.

So, with this modification to the solution provided by @yuval-itzchakov you'll be able to capture underlying exceptions:

var subtask = Next.Invoke(context);

await subtask;

if (subtask.Exception != null)
{
    // log subtask.Exception here
}
Michael
  • 430
  • 3
  • 8