Click here to Skip to main content
15,885,216 members
Articles / Web Development / ASP.NET
Tip/Trick

Handling JSON Errors in OWIN Web Application

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
8 Feb 2018CPOL3 min read 11.8K   44   2  
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:

C#
[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:

C#
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:

C#
[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:

C#
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)


Written By
Software Developer (Senior) Finstek
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --