How to bypass Episerver's Content Delivery API when doing Ajax requests

Published on in C#, Episerver and JavaScript

Requests with the Accept header set to application/json are automatically routed to Content Delivery API. Here's how to bypass it and make the request go to a page controller instead.

Starting from Content Delivery API version 2.3.0, requests with the Accept header set to application/json are automatically routed to Content Delivery API. This is called "Using friendly URLs" in the documentation.

This is a nice feature. But what if you wanted to make an Ajax request to a page controller instead? Let's say you have a Single Page Application and you want to retrieve the next page's data (in JSON format) from the page type's controller using an Ajax request. (This is actually what we are doing at work.)

In the controller, you can do something like this:

public class MyPageController : PageController<MyPage>
{
public ActionResult Index(MyPage currentPage)
{
if (Request.IsAjaxRequest())
{
var data = /* ... */;
return Json(data);
}

return View("Index", new MyPageViewModel(currentPage));
}
}

But, because the Ajax request's Accept header will be set to application/json, the request is automatically routed to Content Delivery API, and the request never reaches the controller.

We can't change the value of the Accept header to another MIME type because we want the data in JSON format. What can we do?

Turns out that we can specify a relative quality value for the MIME type: application/json;q=1. From MDN:

Quality values, or q-values and q-factors, are used to describe the order of priority of values in a comma-separated list. It is a special syntax allowed in some HTTP headers and in HTML. The importance of a value is marked by the suffix ';q=' immediately followed by a value between 0 and 1 included, with up to three decimal digits, the highest value denoting the highest priority. When not present, the default value is 1.

Now that the value of the Accept header doesn't strictly equal to application/json, the request is not routed to Content Delivery API. Instead, it reaches the controller. Egg-cellent!

Since we are specifying only one MIME type, plus since the default quality value is anyway 1, appending ;q=1 to the Accept header doesn't have any side effects.

You should probably document this somewhere. For example, if you are using fetch(), you can write something like this:

function getPageData(url) {
return fetch(url, {
headers: {
// Requests with the `Accept` header set to `application/json`
// are automatically routed to Content Delivery API.
// The `;q=1` here is a "relative quality value"
// and doesn't have any effects other than
// tricking the request to go to the controller
// instead of Content Delivery API.
Accept: 'application/json;q=1',
},
}).then((response) => response.json())
}

This way some overly enthusiastic developer won't remove the quality value thinking that it's unnecessary (since there's only one MIME type and since the default quality value is anyway 1).