EPiServer Content Delivery Api only returns 404 NotFound

Lately I spent some time investigating 404 NotFound responses from an EPiServer Content Delivery Api installation. While the SiteDefinition Api URL worked just fine, the problem was that the API returned 404 NotFound for all contents requested.

Here are some pointers to determine possible reasons. Thanks to Svante Seleborg for helping out with debugging.

EDIT: Matthew Mols have written an useful article for Getting started with the Episerver Content Delivery API.

EPiServer’s Content Delivery Api requires a “Content Api Access” role

If you have a look in EPiServer’s ContentApiOption implementation using for instance ILSpy you’ll notice that there is a hard coded role name in the RequiredRole property.

EPiServer's implementation of the IContentApiOption interface.

If you do not have this role set with READ access on the EPiServer pages that you are trying to retrieve through the API, you will get a 404 NotFound error. This is because the ContentApiController (the one responding to your content requests) will be using a RequiredRoleFilter to filter your pages.

As a short side note, there is also a SiteFilter in place to ensure that you are indeed using the proper site.

If any of these two filters reports your page as invalid, or if you simply entered some invalid reference, EPiServer will throw a ContentNotFoundException not really telling you the reason.

So the reason why your site-URL works is that it uses it’s own controller (SiteDefinitionApiController) which does not filter away your stuff.

Note that you can easily swap EPiServer’s implementation if the IContentApiOption to your own should you find the need to.

EPiServer’s Content Delivery Api requires proper initialization

So, you have created a role called Content Api Access, set it with READ access on the start page and inherited it down through the entire site just for good measure (you really shouldn’t, your site would be leaking). However, it still doesn’t work.

If you have a look in the supplied implementation of the IContentApiRequiredRoleFilter mentioned before (again using ILSpy), you will notice that EPiServer will attempt a constructor injection of the IContentApiOptions implementation. This will only work if there actually is an implementation to be injected.

This, you will need to set up yourself. Luckily, EPiServer has provided us with two useful extension methods on the ServiceConfigurationContext object.

ContentDeliveryApiInitializer.cs

[InitializableModule]
[ModuleDependency(typeof(ServiceContainerInitialization))]
public class ContentDeliveryApiInitializer : IConfigurableModule
{
  private readonly ILogger _logger;

  public ContentDeliveryApiInitializer()
  {
    _logger = LogManager.GetLogger(this.GetType());
  }

  void IConfigurableModule.ConfigureContainer(ServiceConfigurationContext context)
  {
    _logger.Information("Initializing Content Api");
    context.InitializeContentApi();

    _logger.Information("Initializing Content Search Api");
    context.InitializeContentSearchApi();
  }

  void IInitializableModule.Initialize(InitializationEngine context) { }
  void IInitializableModule.Uninitialize(InitializationEngine context) { }
}

All you really need to do is create an initializer, like the one above, and have the ConfigureContainer method make calls to these initializers.

EPiServer's InitializeContentApi extension method.

If you have a look at what they’re doing, it’s not something you couldn’t do yourself. However, you don’t have to. It’s just a matter of adding stuff to the container together with an options implementation.

EPiServer's InitializeContentApiSearch extension method.

EPiServer’s Content Delivery Api requires proper routing

So what if it still doesn’t work? Did you enable CORS and set up the correct routing yet? If not, this may be done through the GlobalConfiguration in Global.asax.cs.

Global.asax.cs

public class EPiServerApplication : EPiServer.Global
{
  protected void Application_Start()
  {
    // ...
    GlobalConfiguration.Configure(WebApiConfig.Register);

If you have other APIs in your EPiServer installation, like endpoints for retrieving stuff using AJAX, you may already have other set up in the Register method below.

WebApiConfig.cs

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // If you have other APIs you may need other stuff here as well.

    config.MapHttpAttributeRoutes();
    config.EnableCors();
  }
}

The above implementation is the minimum requirement for getting the EPiServer Content Delivery Api routing to work.