Web API Signatures with Multiple Complex Parameters

I’ll qualify this post up front by say it is probably going to be more of a “rant” than most. That being said, I am very big fan of ASP.NET Web API, and am looking forward to see the enhancements coming in Web API 2.

The Problem

In the current version of Web API, one of my biggest pet peeves is the usage of multiple complex objects within the Web API method signature is basically not allowed. If you are not too familiar with Web API usage, then right now you are probably asking “what the heck is he talking about”? Take a look at this standard Web API Post method:

Capture

First thing you’ll notice is that there are two complex objects in the signature. In this example I want to pass two completely different instances of the customer object to the method for comparisons and who knows what else. Unfortunately upon executing this, you’ll notice that “customerToCompare” is ALWAYS NULL. This is because only one complex object can be parsed using the model binder from the body for one request. In the above example I have specified “[FromBody]” attribute manually for the purpose of the demo, but it is not required as complex objects by default are always parsed from the body of the request. I could manually specify that a Customer should be parsed “[FromUri]”, but I think we can agree that’s not a good idea!

For performance reasons, the Web API request body is only allowed to be accessed and parsed “once”. So after the scan and parsing occurs of the request body for the “customer” parameter, all subsequent body parses will end in “NULL”.

To reiterate in Web API:
– Only ONE item can be attributed with “[FromBody]”
– Any number of items can be attributed with “[FromUri]”

The next legitimate comment I can already hear you making is that “I am simply using Web API for an unintended purpose”. There may be some merit in that if we assume that Web API was created for use in RESTful services only. Web API is at its core, simply a pure technology for dealing with HTTP requests. And on top of that REST is a pure architectural style for using on top of HTTP, and Web API is a prime technology used to implement this style. That being said, I have some very specific views, that Web API should be (and can be) a replacement for any other types of Web Services you have used in the past in .NET. No need to use .ASMX or WCF Web Services anymore, especially when Web API is SO SIMPLE and SO PURE in web communication.

I use Web API as a replacement for RPC, REST, and OData services; one technology for every style/specification. If an application I’m writing is internal only and there is no possible need for full REST architecture then I simply use RPC calls like I would have 5 years ago into an .ASMX service. Letting the requirements dictate the architecture I think is an easy lesson we should all be familiar with.

I won’t go any deeper here, but do check out this article:
http://encosia.com/rest-vs-rpc-in-asp-net-web-api-who-cares-it-does-both/

The Solution

While I have titled this “the solution” really, what we are talking about is a possible workaround for dealing with multiple complex objects in the signature.
The key in this workaround lies in using the “JObject” object apart of Newtonsoft.Json library. This objects gives us a concrete type specifically designed for working with JSON.

Capture2

We simply adjust the signature to accept one complex object from the body, our “JObject”, which we have pluralized to “customers”. From here we will manually parse properties of the JSON object and use generics to hydrate the concrete “Customer” type. So a couple extra lines of code and I can parse as many complex objects as I want with relative ease. But honestly, I’m still not a big fan of this approach:

– One of the best things about Web API is how clean it is and how simple it is. I love that you can live and die by your method signatures for contracts to the API. In this example with JObject, I have no idea what the true contract of this method is simply by looking at the signature. I find it messy and a bit of a hack. That being said I do see true scenario’s where using a JObject allows you to be a lot more dynamic.
– While I am concerned about readability and maintainability of the API, realistically in this approach I have killed my full content negotiation functionality that we get for free from Web API. No matter what I decide to use for my “content-type” in the request, I will have an embedded JSON string within the content. That’s not so bad when I am only using JSON, but if it were a public API and someone wanted to use XML, they would basically be passing an XML payload with a JSON string inside of it.

On initial launch of Web API a while ago I heard rumblings of support for parsing the body more than once in future releases, but I have seen zero mention of it in Web API 2 as of yet. I will have to test it out and get back to you on that!

17 Comments

  1. anon says:

    what if one of your arguments is a List of objects?

    • travis says:

      You can consume a list of objects for sure. It would be considered a complex type, no different then a custom model object. Thus you could only consume one complex type or list in the arguments. Keep in mind, that the list of objects would all need to be the same type List as normal.

  2. Tom says:

    Brilliant Travis, this is exactly what I needed!

  3. BarrettMactier says:

    Thanks for ones marvelous posting! I certainly enjoyed reading it, you might be a great author.I will be sure to bookmark your blog and will come back down the road. I want to encourage you continue your great posts, have a nice afternoon!

  4. Ludo Goarin says:

    Travis, this is great. This might have been an actual intent and the reason why you only have 1 complex type allowed. Makes so much sense… The tech space is moving faster because of schema less data stores, that means models on the client and server sides need to evolve fast and often.

    I was initially annoyed to have to create a class for each combination of models to be passed to my Web API actions. With this concept, I don’t have to. I can simply reuse my existing object classes and combine them in my JSON to be passed to the API. Very cool and flexible.

    So… thank you!

    • Qamar says:

      You param can always be type of dynamic, you don’t necessarily have to create a concrete type. I suspect JObject under the hood uses same technique

  5. SamD says:

    Thanks for the well written post and indeed it is very helpful.
    Could you please help me with following issue?
    Suppose that I’m generating Help Pages for my REST APIs.
    (i.e. as mentioned in here: http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/creating-api-help-pages )
    In that case how can I generate these help pages with the the details of complex parameters?

    • travis says:

      Hey SamD…. for obvious reasons I don’t believe there is a way to auto generate “helpful” documentation about what exactly is inside your complex object signature simply because that information must either be inferred from code or by further examination of the object.

      However, if your already in the habbit of using XML code documentation, one of the easiest methods to achieve what you want would be add documentation in XML comments just above the methods in your Api Controller. In the link you sent me: http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/creating-api-help-pages look at the section titled: Adding API Documentation

  6. bob says:

    I’ve included the Newtonsoft.Json namespace in my cs controller, however c# is not recognizing JObject type. In other words, I’m trying to following your method signature above – .[FromBody]JObject .
    What am I missing ?
    thanks,
    Bob

  7. Bob says:

    okay, I found the solution. The correct namespace is using Newtonsoft.Json.Linq;
    Thank you .

  8. Bob says:

    Hi Tim,
    Great post, by the way.
    I’m using this method signature below to first read in my post parameters, then execute an internal api method within a DLL that exposed to me :

    public HttpResponseMessage Post([FromBody]JObject drmObject)
    {
    string sid = drmObject.GetValue(“sid”).ToString();
    // read more values here…

    string response = DynAggrClientAPI.insertDRMCategory(sid, riskMeasCategory); // internal API
    }

    Now I have multiple API methods that i need to call (various insert methods, update/delete methods, etc.).

    I was thinking of adding a special post parameter (coming from the web client) which would embed the api method name. This way I can determine what the front-end client is trying to do.

    If not this way, then is there another way I can add multiple POST methods in my C# API controller ?

    I’m a little confused as to the best way of handling it.

    regards,
    Bob

    • travis says:

      Hey Bob,

      Your noted issue actually comes up often in development. Web API is designed around utilizing REST based API calls as it makes sense to do so – (i.e. using DELETE, GET, POST, PUT, and PATCH verbs). That works create for standard type of CRUD behaviour, but when your into lots of different custom business operations your methods likely don’t fit 1 to 1.

      So here is what you can do:
      1. Analyze if it makes sense to utilize any of the other verbs (i.e. PUT, DELETE, etc for your operations).
      2. If you need two POST actions, then you can do that if you have a different signature in the URL (just overload the method). Ensure what you are overloading by is not a complex type (it must be a type that changes the URL – such as any primitive).
      3. Depending on the version of Web API you are using you could also use attribute routing to have two Post methods with the same signature in the same class at different endpoints available (with the same signature you’ll need to change the name of the method slightly as well to be valid).

      Based on your description above, I think a combination of proper VERB analysis, and method overloading should work like a charm for you.

      Travis

  9. tejas says:

    “An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.” Got this type of error while passing Jobject in rest API

  10. Amin says:

    Hi Travis,
    This was what I needed.
    Your post helped me to save my time.
    Thanks.

  11. Ranji says:

    Or just use Dictionary as parameter and add required objects in it

  12. Anil says:

    Ideally you should create specific request class (i.e. accepting two entities to compare).
    This will be helpful for the documentation generating tools to identify expected input type.

Leave a Reply to Bob