Skip to the content

Using Swagger in Umbraco 8

At Code Wizards, we love Umbraco. It's a fantastic flexible CMS and, for us, it's the right tool for the job 99% of the time.

But if you've not spent a lot of time with Umbraco it can sometimes be a little unclear how you can add middleware into the OWIN pipeline.

Especially when a new major version of Umbraco is released and things have drastically changed.

 

Umbraco 8

Umbraco 8 was released on the 26th of February.

"A new major Umbraco release with new beneficial features, a fresh new look and a thorough clean up of the codebase."

I'm loving Umbraco 8 and, in my opinion, the people at Umbraco have done an amazing job.

A lot has changed though and whilst some of the documentation has been updated, there are definitely areas that haven't received quite as much love.

I'm confident the docs will be fleshed out in time, but until then I hope that I can shed some light on how to make Umbraco play nicely with swagger.

 

Setup

Before we continue I want to detail what I've done so far to get to my starting point: 

  • I've setup a blank asp.net web application using .net 4.7.2
  • I've installed and setup Umbraco 8.0.0 with the out-of-the-box template (this gives us some front end and some pre-setup documents in the backend).
  • I've changed the Umbraco Models mode to "AppData" (in the web.config app settings), generated the models via the Umbraco settings page and included the generated files in /App_Data/Models/

If any of this sounds totally foreign there's documentation on Umbraco's website and I can confirm that these processes have not substantially changed between v7 and v8.

Now we're at the same point, let's get started.

 

Controller / Model / Documents

I'm going to be working with the Products documents that are in the Umbraco starter template but you can use what ever you'd like.

The document or document type are not really relevant here - it's more about proving that we can create a working API which returns information pulled from Umbraco documents and then produce Swagger docs for the controller.

Here's my products controller:
using System.Linq;
using System.Web.Http;
using Umbraco.Web;
using Umbraco.Web.PublishedModels;
using Umbraco.Web.WebApi;
using UmbracoSwagger.Features.Products.Models;

namespace UmbracoSwagger.Features.Products
{
    [RoutePrefix("api/products")]
    public class ProductsController : UmbracoApiController
    {
        [HttpGet]
        [Route("")]
        public IHttpActionResult GetProducts()
        {
            var products = Umbraco.ContentAtRoot().DescendantsOrSelf<Product>();

            var models = products.Select(p => new ProductApiModel
            {
                ProductName = p.ProductName,
                Category = p.Category.ToList(),
                Description = p.Description,
                Price = p.Price,
                Sku = p.Sku
            });

            return Json(models);
        }
    }
}

Nothing too exotic here. I'm going to assume you already know how use WebApi.

If you run the solution and browse to api/products the first thing you'll notice is that you get a 404.

The reason for this is that, out of the box, Umbraco does not support mapping Http attribute routes.  So how do we fix that?

 

UserComposers

And here we really get to the meat of the subject. 

In Umbraco 7 there was the concept of ApplicationEventHandlers. A class that inherited from ApplicationEventHandler would be called during Umbraco's initialisation and here you could perform certain tasks, such as adding middleware.

ApplicationEventHandlers don't exist in Umbraco 8 however, they've been replaced by "UserComposers".

Let's take a look: 
using System.Web.Http;
using Umbraco.Core.Composing;

namespace UmbracoSwagger.App_Start
{
    public class WebApiComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            GlobalConfiguration.Configuration.MapHttpAttributeRoutes();
        }
    }
}

 

The great thing about UserComposers is that they're not only a way of hooking in to the initialisation pipeline of Umbraco, they're also the gateway into Umbraco's DI framework.

That's right - proper DI out of the box in Umbraco 8. Just Register your services into the Composition and they'll be automatically injected!

 

We're not going to use the composition here, instead we're going to configure WebApi to use attribute routing by calling the MapHttpAttributeRoutes method on the global HTTP configuration.

Run and browse api/products and we have some JSON! 👌

 

Swashbuckle

Great, we have a working API now, how to we get swagger working?

Firstly we'll need to install the "SwashBuckle.Core" nuget package. 

SwashBuckle is fantastic nuget package by DomainDrivenDev that allows us to add swagger documentation and swagger seamlessly in to our WebApi project.

Check out the Swashbuckle GitHub for the full documentation.

Once installed, we'll need to add it to our http configuration. Create a new UserComposer and add the following code:

using System.Web.Http;
using Swashbuckle.Application;
using Umbraco.Core.Composing;

namespace UmbracoSwagger.App_Start
{
    public class SwggerComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            GlobalConfiguration
                .Configuration
                .EnableSwagger(c =>
                {
                    c.SingleApiVersion("v1", "MY API");
                })
                .EnableSwaggerUi();

        }
    }
}

Run and browse /swagger and you'll notice that you have a fancy swagger UI!

Unfortunately though you'll notice that swagger is reporting an error loading your API configuration:

"Multiple operations with path 'umbraco/backoffice/UmbracoApi/Content/GetEmpty/{id}' and method 'GET'". This happens as Umbraco it's self has some of these "conflicting endpoints".

We can fix this by adding the following line to the EnableSwagger configuration:

 c.ResolveConflictingActions(a => a.First());

Run and browse /swagger and our documentation is working!!

 

But what are all of these end points?!

These are all of the endpoints that are exposed by Umbraco. 

If you're happy with having all of the Umbraco endpoints in your swagger docs, great! You're done!

 

DocumentFilter

If you're like me however, having all of these endpoints mixed in with your beautiful succinct REST API is triggering your developer rage!

Luckily we can filter out all of the Umbraco endpoints from our swagger docs with an implementation of IDocumentFilter:

using Swashbuckle.Swagger;
using System.Linq;
using System.Web.Http.Description;

namespace UmbracoSwagger.App_Start
{
    public class SwaggerDocumentFilter : IDocumentFilter
    {
        public void Apply(
SwaggerDocument swaggerDoc,
SchemaRegistry schemaRegistry,
IApiExplorer apiExplorer) { swaggerDoc.paths = swaggerDoc .paths .Where(x => x.Key.StartsWith("/api")) .ToDictionary(e => e.Key, e => e.Value); } } }

We then need to add it to our EnableSwagger config like so:

c.DocumentFilter<SwaggerDocumentFilter>();

This implementation of the IDocumentFilter filters out everything but the routes that start with /api.

I like having all of my API running under /api but you might be different and you can change the filter here to suit your needs.

Run and browse again /swagger and voila! We just have just our Products endpoints:

Almost done

The last thing we need to add is a decoration to our GetProducts Action to instruct Swagger what the response type looks like,

Add the below attribute to your GetProducts action (or what ever you've called it)

[ResponseType(typeof(List<Product>))]

Run and browse /swagger again and you'll notice that the model example value now looks like your api model:

[
  {
    "productName": "string",
    "price": 0,
    "category": [
      "string"
    ],
    "description": "string",
    "sku": "string"
  }
]

 

Conclusion

And that's it! We have a working WebApi controller in our Umbraco solution with full swagger documentation and UI!

The key difference here is the switch from ApplicationEventHandler to IUserComposer which really wasn't very hard to translate. Without documentation this certainly could have take a while to figure out.

 

I hope this helps any one upgrading to or looking to start a new project using Umbraco 8.

About the author

Lee Higgitt

Lee Higgitt

My experience has been realising retail and ecommerce initiatives. I specialise in high transaction code and user interface implementations.

My passions are low friction user interfaces and maximising enjoyment of systems.  I also love games of all sorts.

comments powered by Disqus

We're Honestly Here to Help!

Get in Touch

Got questions?  We're always happy to (at least) try and help.  Give us a call, email, tweet, FB post or just shout if you have questions.

Code Wizards were very efficient and transparent. It was a pleasure working with them as they brought valuable advice, insight and development to achieve our project's success.

Daniel Ross, Head of IT and Development, 52 Entertainment / Virtual Regatta

Code Wizards are a pleasure to work with.

We've challenged their team to integrate our cutting edge AI, AR and VR solutions and always come up trumps.

I love their approach and help on strategy.

Richard Corps, Serial Entrepreneur and Augmented Reality Expert

Working In Partnership

We're up for (almost) anything.

We believe that life is better in co-op (and if we can't help then we probably know somebody else that we can direct you to).  Get in touch.