Custom Operation Names with Swashbuckle 5.0

This is a post about Swashbuckle –  a .NET library that seamlessly adds Swagger support to WebAPI projects.  If you aren’t familiar with Swashbuckle then stop reading right now and go look into it – it’s awesome.

library-api

Swashbuckle has recently released version 5.0 which includes (among other things) a ridiculous array of ways to customise your generated swagger spec.

One such customisation point allows you to change the operationId (and other properties) manually against each operation once the auto-generator has done it’s thing.

Why Bother?

Good question.  For me, I decided to bother for one very specific reason: swagger-js.  This library can auto-generate a nice accessor object based on any valid swagger specification with almost no effort, whilst doing lots of useful things like handling authorization and parsing responses.

swagger-js uses the operationId property for method names and the default ones coming out of Swashbuckle weren’t really clear or consistent enough.

Injecting an Operation Filter

The means for customising operations lies with the IOperationFilter interface exposed by Swashbuckle.

public interface IOperationFilter
{
  void Apply(Operation operation, 
    SchemaRegistry schemaRegistry, 
    ApiDescription apiDescription);
}

When implemented and plugged-in (see below), the Apply method will be called for each operation located by Swashbuckle and allows you to mess around with its properties.  We have a very specific task in mind so we can create a SwaggerOperationNameFilter class for our purpose:

public class SwaggerOperationNameFilter : IOperationFilter
{
  public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
  {
    operation.operationId = "???";
  }
}

When you installed the Swashbuckle nuget package it will have created a SwaggerConfig file in your App_Start folder.  In this file you will likely have a long and well-commented explanation of all available configuration points, but to keep things simple we can insert the reference to our filter at the end:

GlobalConfiguration.Configuration
  .EnableSwagger(c =>
  {
    //...
    c.OperationFilter<SwaggerOperationNameFilter>();
  });

Getting the Name

At this point you have a lot of flexibility in how you generate the name for the operation.  The parameters passed in to the Apply method give you access to a lot of contextual information but in my case I wanted to manually specify the name of each operation using a custom attribute.

The custom attribute itself contains a single OperationId property…

[AttributeUsage(AttributeTargets.Method)]
public sealed class SwaggerOperationAttribute : Attribute
{
  public SwaggerOperationAttribute(string operationId)
  {
    this.OperationId = operationId;
  }

  public string OperationId { get; private set; }
}

…and can be dropped onto any action method as required…

[SwaggerOperation("myCustomName")]
public async Task<HttpResponseMessage> MyAction()
{
  //…
}

Once the attributes are in place we can pull the name from our filter using the ActionDescriptor

operation.operationId = apiDescription.ActionDescriptor
  .GetCustomAttributes<SwaggerOperationAttribute>()
  .Select(a => a.OperationId)
  .FirstOrDefault();

Voila!

Advertisements

Hiding ProxyApi Routes from Web API Help Pages

If you are using ProxyApi and you have tried out the Web API Help Pages feature then you will have noticed a bunch of duplicate routes showing up for all of your actions that look something like this:

GET /api/{proxy}/Controller/Action?foo=bar

ProxyApi needs to be certain of the Route-to-Controller/Action mapping in order to correctly generate the JavaScript proxies, and it achieves this by inserting a custom route at the start of the route table so that it will always take precedence (if matched).

Unfortunately the Web API ApiExplorer finds these routes, maps them to the action and generates a duplicate route for every action in your API!

Getting Rid of the Routes

Thankfully it is very simple to filter these out.  When you add the Web API help pages package to your project it will generate a LOT of code that builds and renders the help page content.  This gives you plenty of entry points in which you can intercept and hide the ProxyApi-specific routes.

For our purposes here we can subclass the ApiExplorer class and filter out any route that contains “{proxy}”.

public class CustomApiExplorer : ApiExplorer
{
  public CustomApiExplorer(HttpConfiguration config) : base(config)
  {}

  public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
  {
    if (route.RouteTemplate.ToLower().Contains("{proxy}"))
      return false;

    return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
  }
}

Now we just need to plug this implementation in instead of the default…

//in your help page configuration
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));

…and we’re done!

ProxyApi & Anti-Forgery Tokens

Anti-Forgery Tokens?

Good question.  Anti-forgery tokens are a recommended way of preventing one of the OWASP Top Ten security vulnerabilities: Cross Site Request Forgery, or CSRF.

CSRF works on the basis that once you have logged into YourSite using your browser, any request to that domain will share the authentication information.  Normally, requests to YourSite would come from YourSite, but other developers are perfectly capable of writing some code on their site that tries to make a request to YourSite to do something evil.

Though there are a few ways of preventing or reducing the risk of CSRF attacks, anti-forgery tokens are the currently recommended approach.

So how do they work?  Whenever the server serves up a page that may result in a submission (e.g. a page that contains a form) it sets a randomly-generated cookie value.  The client must then include the random value in both a hidden form field and the request cookie; otherwise, the server will reject the request as invalid.  Attackers will not be able to read the cookie value; therefore they cannot include it as a form field and so their attack fails.

ASP.NET MVC Implementation

MVC makes it very easy to implement anti-forgery tokens.  Very easy.

Step 1: add an attribute to your action or controller

[ValidateAntiForgeryToken]
public ActionResult DoSomething()
{
    //…
}

Step 2: include the following within the form on the page

@Html.AntiForgeryToken()

Unfortunately WebAPI does not have a similar implementation, but there are thankfully a lot of examples out there (e.g. Kamranicus’ example & the MVC SPA template ) of how to achieve similar functionality that works with WebAPI.

So how can we adapt these ideas to work with ProxyApi?

ProxyApi Implementation

The intention of this library is to allow you to quickly create proxy classes for WebAPI methods; because it is expected to be running in the browser (it generates JavaScript, after all) it will be using cookie authentication and should therefore consider CSRF.

Ideally, the developer using the library doesn’t want to do anything more than they do for their MVC implementation, so it would seem like that is a good convention to follow.

Setting The Token

As with MVC, setting the cookie token and inserting the hidden form value onto the page is done by calling the Html.AntiForgeryToken() method in your view.  This is deliberately identical to the MVC method to keep things as consistent as possible.

Decorating the Controller

Following the same pattern as MVC and the examples listed above, the ProxyApi implementation uses an attribute that can be specified against a controller or an action:

[ValidateHttpAntiForgeryToken]
public void PostSomething(Something data)
{
    //...
}

This attribute is an extension of AuthorizationFilterAttribute that uses the cookie- and hidden tokens to validate the request.  The second value – the one that would normally be included as a hidden form field – is instead expected as a custom header value: X-RequestVerificationToken.  This approach avoids complications in combining the ProxyApi automatically-generated POST data and a custom form field.

Because WebAPI is often used for non-browser-based access, the attribute also allows you to optionally specify any types of authentication (e.g. Basic) that should be excluded from the verification process.

Passing the Hidden Token to the Server

The JavaScript implementation of the proxy objects allows you to specify either a concrete value or an accessor function to get the form field value:

$.proxies.myController.antiForgeryToken = "1234abc";

// or

$.proxies.myController.antiForgeryToken = function() { 
    return $("#someField").val();
};

By default, this function will use jQuery to locate the hidden input generated by the Html.AntiForgeryToken() method and use it’s value.

Summary

Overall, this implementation is nothing groundbreaking.  It borrows heavily from the the SPA MVC template and from other examples online but it does allow ProxyApi to prevent CSRF attacks with minimal change to the code for developers.

The source code for this is available on GitHub, and the updated package is available for download via nuget.

Exception Handling for Web API Controller Constructors

The generally-recommended best practice for exception handling within Web API is to to use exception filters.  Once registered, these classes sit in the processing pipeline for a message and can react to exceptions that are thrown by actions.

A Problem

The issue with the statement above is the qualifier “by actions”.  While an exception filter will correctly handle any errors thrown from within an action method, it will be bypassed by exceptions thrown during the creation of the controller.

These exceptions include two categories of error: exceptions thrown from within the controller constructor, and a failure to locate or invoke an appropriate constructor.  The latter problem is, for me, the more common – I use the Autofac MVC & WebAPI integrations (highly recommended, by the way) to handle dependency injection in controllers, and there are quite often scenarios where one of the dependencies is not available.  In these cases I really need a way to catch and to nicely handle those exceptions.

One way in which we can achieve this lofty aim is by creating a custom implementation of IHttpControllerActivator.

The Controller Activator

The IHttpControllerActivator interface only contains one method:

IHttpController Create(
	HttpRequestMessage request,
	HttpControllerDescriptor controllerDescriptor,
	Type controllerType
)

This method is responsible for creating and returning an instance of a specified controller before the API action is invoked.  This is perfect for our scenario because it is a very specific responsibility; we need a custom implementation, but we will not have to worry about how the controller type is selected, how the action is selected or how it is invoked.

Implementing a Decorator

To be honest, we don’t really want to get into how the controller is actually created – we just want to wrap it in a try { … } catch { … } – so instead of creating our own activator we should just write a decorator pattern to wrap the existing implementation.

public class ExceptionHandlingControllerActivator : IHttpControllerActivator
{
	private IHttpControllerActivator _concreteActivator;

	public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator)
	{
		_concreteActivator = concreteActivator;
	}
		
	public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
	{
		try
		{
			return _concreteActivator.Create(request, controllerDescriptor, controllerType);
		}
		catch
		{
			//custom handler logic here
		}
	}
}

This simple class constructs on a concrete instance of IHttpControllerActivator, then calls down to that concrete instance within a try/catch block.  We can then implement our custom exception handling in the catch.

Now all we need to do is replace the default activator with our one.

Hooking It Up

We need to tell Web API to use our new controller activator instead of the default, and (as with so much else in Web API) we do this through the HttpConfiguration object; specifically, the Services property.

This comes with a convenient Replace method that allows us to insert our implementation in place of the default version.  We also want to pass that default into the constructor of our class, so we end up with something like this:

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), 
	new ExceptionHandlingControllerActivator(
		GlobalConfiguration.Configuration.Services.GetHttpControllerActivator()
	)
);

It looks a little messy, but it’s not complicated: grab a reference to the current activator, pass it into our decorator, then pass that into the Replace method.

Simple!

ProxyApi: Automatic JavaScript Proxies for WebAPI and MVC

Taking Inspiration from SignalR

One of my favourite features of SignalR is the the automatic generation of JavaScript proxies for hub methods. By adding a hub class in C#…

//server
public class ExampleHub : Hub
{
	public void SendMessage(string message)
	{
		//do something with message
	}
}

…you can get the JavaScript wrapper for that method just by adding a reference to /signalr/hubs:

//client
$(function () {
    var hub = $.connection.exampleHub;

    $.connection.hub.start(function() {
        hub.sendMessage("a message from the client");
    });
});

This is such a useful feature that it has even been suggested as an alternative to Web API controllers.

But why should we use hubs as a replacement for MVC or Web API controllers? Why can’t we instead write similar functionality to work with controllers?

Introducting ProxyApi

ProxyApi is a small NuGet package that automatically generates JavaScript proxy objects for your MVC and Web API controller methods.

PM> Install-Package ProxyApi

(as an aside, this was my first attempt at creating a NuGet package and it was embarrassingly simple).

Once you’ve installed the NuGet package you just need to add a link to ~/api/proxies and the JavaScript API classes will be automatically created.

So what do these actually give you?

API Controllers

Let’s say you have started a new MVC4 project and you add a new “API controller with empty read/write actions”:

public class DataController : ApiController
{
    // GET api/data
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/data/5
    public string Get(int id)
    {
        return "value";
    }

    // POST api/data
    public void Post([FromBody]string value)
    {
    }

    // PUT api/data/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/data/5
    public void Delete(int id)
    {
    }
}

Ordinarily if you wanted to call these methods from JavaScript you would need to write something like the example below using jQuery:

$.post("/Data/123", function(data) {
    //do something with the result
});

This is actually pretty concise, but I have 2 problems with this approach:

  1. The code calling this method knows that it is making a POST call. What do I do when I want to switch my data source to local storage, or some other data accessor?
  2. The code knows about the URL.

Instead, what I would prefer is a JavaScript proxy object on which I can call a method – passing in appropriate parameters – without ever knowing where that method gets it’s data or how it does so. And this is what ProxyApi provides.

Add a new script tag to _Layout.cshtml to ~/api/proxies (after the jQuery reference) and you can start directly calling API methods without writing another line of code yourself:


$.proxies.data.get().done(function(allItems) {
  //allItems will contain ['value1', 'value2']
});

$.proxies.data.get(123).done(function(item) {
  //item will be 'value'
});

//will send 'value' to Post method on controller
$.proxies.data.post("value");

//will send id=1, value='value' to Put method on controller
$.proxies.data.put(123, "value");

//will send 123 to Delete method on controller
$.proxies.data.put(123, "value");

These proxy objects can now be passed to any code that needs to perform data access without ever exposing how that data is sourced. You can easily mock them for unit testing, replace them if needed, and call them without needing to know where the website is hosted.

Complex Types

This is all well and good for simple data types like the strings in the example above, but what about when you want to manipulate complex types?

public class Person
{
	public int Id { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
}

public class DataController : ApiController
{
    [HttpPost]
    public void UpdatePerson(Person value)
    {
    }
}

In this case, just pass a JSON object to the generated method:

$.proxies.data.updatePerson({
    Id: 123,
    FirstName: 'Steve',
    LastName: 'Greatrex'
});

This will send the JSON object as POST data to the Post method on the controller.

And when you have both URL and body data, such as in the auto-generated Put method? Just decorate the parameters with [FromBody] or [FromUri] and the rest will be taken care of:

public void Put([FromUri]int id, [FromBody]Person value)
{
}

Note: it generally isn’t necessary to use [FromUri] as ProxyApi will assume that anything is a URL parameter unless told otherwise. The only exception to this is for POST methods that take a single parameter, which will be assumed to be POSTed.

Non-Conventional Method Names

All the examples so far have been using conventionally-named methods, but there is no requirement for this: any method name will work:

public class DataController : ApiController
{
	public void DoSomething(int id)
	{
		// --> $.proxies.data.dosomething(123) (GET)
	}

	[HttpPost]
	public void DoSomethingElse(Person person)
	{
		// --> $.proxies.data.dosomethingelse({ ... }) (POST)
	}
}

Appropriate HTTP verbs will be used for any method based on the following rules (in priority order):

  • [Http*] attribute (e.g. [HttpPost], [HttpGet] etc.)
  • [AcceptVerbs(...)] attribute
  • Method naming conventions, e.g. DeletePerson() == DELETE
  • GET for everything else

You can also specify custom names for both the proxy objects and methods using the [ProxyName] attribute.

[ProxyName("custom")]
public class DataController : ApiController
{
	[ProxyName("method")]
	public void DoSomething(int id)
	{
		// --> $.proxies.custom.method()
	}
}

Excluding and Including Elements

By default, ProxyApi will automatically include every method in every type in the current AppDomain that inherits from either System.Web.Mvc.Controller or System.Web.Http.ApiController. You can change this behaviour to exclude everything by default by changing the ProxyGeneratorConfiguration.Default.InclusionRule property:

ProxyGeneratorConfiguration.Default.InclusionRule = InclusionRule.ExcludeAll;

You can also explicitly include or exclude any element by decorating it with one of the [ProxyInclude] or [ProxyExclude] attributes:


[ProxyExclude] //excludes entire controller
public class ExcludedController : ApiController
{}

[ProxyInclude] //includes entire controller and all methods...
public class IncludedController : ApiController
{
    [ProxyExclude] //...except for explicitly excluded methods
    public void ExcludedMethod() {}
}

public class DefaultController : ApiController
{
    [ProxyExclude] //will always be excluded
    public void ExcludedMethod() {}

    [ProxyInclude] //will always be included
    public void IncludedMethod() {}

    //will be included or excluded based on the globally configured default
    public void DefaultMethod() {}
}

Returning Data & Handling Errors

The generated proxy methods all return an instance of the jQuery $.Deferred object, so you can use the done, fail and complete methods to handle the results from the controller acctions:

$.proxies.person.getAllPeople()
    .done(function(people) {
        //people will contain return value of PersonController.GetAllPeople(), if it succeeds
    })
    .fail(function(err) {
        //this will be called if the controller throws an exception
        //err contains exception details
    })
    .complete(function() {
        //this will be called after success or failure
    });

You can get more information on how to use the jQuery Deferred object from the documentation

MVC Controllers

The examples above are all based around Web API, but everything will work with MVC controllers as well:

$.proxies.home.index().done(function(content) {
    //content will contain HTML from /Home/Index
});

Source Code

I’ve put the source code (including unit tests) on GitHub so feel free to take a look around and make any changes you think are useful.

This is an early version and will probably change quite quickly, so keep an eye out for new developments. If you have any feature suggestions then leave them in the comments (or fork and write them yourself)!