Click here to Skip to main content
13,800,290 members
Click here to Skip to main content
Add your own
alternative version

Stats

4.9K views
26 downloads
2 bookmarked
Posted 9 Feb 2018
Licenced CPOL

Handling JSON Errors in OWIN Web Application

, 9 Feb 2018
Rate this:
Please Sign up or sign in to vote.
In this short article, I'll describe how to configure JSON serializer error handling differently for each request.

Some time ago, I wrote an article, where I discussed how to find typos and usage of obsolete properties in JSON body of a request. The described technique required providing a handler of Error event of the JsonSerializerSettings object. The problem is that this object is shared across all requests to your application, but we need separate handling for each request. In this short article, I'll describe how to do it.

First of all, I assume that you are familiar with the previous article, because I'll use the same model and classes here. You may consider this article as a continuation of the previous one.

Let's take a look at how we usually define Web API application with JSON serialization:

[assembly: OwinStartup(typeof(JsonOwinWebApplication.Startup))]

namespace JsonOwinWebApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();

            config.Formatters.Clear();
            config.Formatters.Add(
                new JsonMediaTypeFormatter {SerializerSettings = GetJsonSerializerSettings()});

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new {id = RouteParameter.Optional}
            );

            app.UseWebApi(config);
        }

        private static JsonSerializerSettings GetJsonSerializerSettings()
        {
            var settings = new JsonSerializerSettings();

            settings.Converters.Add(new StringEnumConverter { CamelCaseText = false });

            settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

            return settings;
        }
    }
}

In our case, we need more complex configuration of JSON serializer. Take a look at the following pseudo code:

private static JsonSerializerSettings GetJsonSerializerSettings()
{
    var settings = new JsonSerializerSettings
    {
        MissingMemberHandling = MissingMemberHandling.Error,
        Error = (sender, args) =>
        {
            handler.Handle(sender, args);
        }
    };

    settings.Converters.Add(new StringEnumConverter { CamelCaseText = false });
    settings.Converters.Add(new ValueJsonConverterWithPath(pathsStack));

    settings.ContractResolver = new ObsoletePropertiesContractResolver();

    return settings;
}

Here, handler and pathsStack should be different for each request. Moreover, the handler should have a reference to the corresponding pathsStack. Also, we must have access to the handler instance in our Web API methods, to get the list of typos and obsolete properties for the current request.

The problem here is that our Startup class configures JSON serializer for all requests, but we need it somehow different for each request. How can we solve this problem?

The first idea I had was about an inversion of control container. I knew, that I can configure lifespan of some objects as "per request". I thought, that everywhere I need unique objects for a request, I'll take them from such a container. Unfortunately, I was not able to implement it. I used Autofac container, I configured it to provide my handler and pathsStack objects per request, I attached the container to the OWIN pipeline and to the HttpConfiguration dependency resolver. But still, I got an error trying to get my objects from the container inside the Error handler of the JsonSerializerSettings. I'm not a big specialist in work with IoC containers, it may be that I made some mistake, so you are welcome to try.

Still, I had a task to solve. At that moment, I thought about OWIN specification. It is very simple and elegant. It describes how your Web application communicates with hosting Web server. The Web server provides your application with an instance of IDictionary<string, object>. The Web server guarantees that the dictionary contains some data (host, path, query, request body, ...). Your application passes the dictionary through the pipeline of middleware. On each stage, you are free to add/remove/modify the content of the dictionary. In the end, your application must add to the dictionary some information about the response (headers, cookies, body, ...). That's it! And this process is repeated for each request.

And here comes my idea. In the beginning of the pipeline, I'll add my objects handler and pathsStack to the dictionary. Later, I'll take them from the dictionary everywhere I need them. Here is how it works:

[assembly: OwinStartup(typeof(JsonOwinWebApplication.Startup))]

namespace JsonOwinWebApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Use(async (context, next) =>
            {
                var pathsStack = new Stack<string>();
                var handler = new TyposAndObsoleteHandlerWithPath(pathsStack);
                handler.Ignore(e => e.CurrentObject is Value && 
                               e.ErrorContext.Member.ToString() == "type");
                handler.AddObsoleteMessage((type, name) =>
                {
                    if (type == typeof(StringValue) && name == "Id")
                        return "Use another property here";
                    return null;
                });

                context.Set("My.PathsStack", pathsStack);
                context.Set("My.TyposHandler", handler);

                await next.Invoke();
            });

            HttpConfiguration config = new HttpConfiguration();

            config.Formatters.Clear();
            config.Formatters.Add(
                new JsonMediaTypeFormatter {SerializerSettings = GetJsonSerializerSettings()});

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new {id = RouteParameter.Optional}
            );

            app.UseWebApi(config);
        }

        private static JsonSerializerSettings GetJsonSerializerSettings()
        {
            var settings = new JsonSerializerSettings
            {
                MissingMemberHandling = MissingMemberHandling.Error,
                Error = (sender, args) =>
                {
                    var owinContext = HttpContext.Current.GetOwinContext();

                    var handler = owinContext.Get<TyposAndObsoleteHandlerWithPath>("My.TyposHandler");

                    handler.Handle(sender, args);
                }
            };

            settings.Converters.Add(new StringEnumConverter { CamelCaseText = false });
            settings.Converters.Add(new ValueJsonConverterWithPath(() =>
            {
                var owinContext = HttpContext.Current.GetOwinContext();

                var pathsStack = owinContext.Get<Stack<string>>("My.PathsStack");

                return pathsStack;
            }));

            settings.ContractResolver = new ObsoletePropertiesContractResolver();

            return settings;
        }
    }
}

In OWIN middleware, I create my handler and pathsStack objects, configure them and add them to the OWIN context using context.Set method. Notice that the constructor of the ValueJsonConverterWithPath class does not accept an instance of paths stack any more, but rather a function, which can return this instance. Again, the reason is that pathsStack object should be different for each request.

Later in any place of my application, I can get OWIN context of the current request using HttpContext.Current.GetOwinContext(). I can use it in my Web API method to get access to the handler object to collect all found typos and usages of obsolete properties:

namespace JsonOwinWebApplication
{
    public class ValuesController : ApiController
    {
        public object Post([FromBody] Value[] values)
        {
            var owinContext = HttpContext.Current.GetOwinContext();

            var handler = owinContext.Get<TyposAndObsoleteHandlerWithPath>("My.TyposHandler");

            // Other processing...

            return new
            {
                Message = "Message processed",
                Warnings = handler.Messages.ToArray()
            };
        }
    }
}

That's it. I hope you'll find this tip useful for the described use case and for other cases, where you need to have separate objects for each Web request.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ivan Yakimov
Software Developer (Senior) Confirmit ()
Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.181214.1 | Last Updated 9 Feb 2018
Article Copyright 2018 by Ivan Yakimov
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid