I don't know how many blog, forum, and Discord posts I've read about how to set up CORS for a headless site in Umbraco. It feels like a lot. None of them worked for me 😥
It's probably just the usual problem of incorrect middleware order, which is always slightly tricky in Umbraco as some of the pipeline configuration is abstracted away.
What am I trying to do?
POST a form, via the Fetch API, from the frontend of my Headless site to my Umbraco instance backend.
The Problem
CORS. It's there to keep us all safe, but it can be a pain to configure.
Fortunately there's a built-in CORS middleware in ASP.NET Core that can be configured
The Catch
I can't modify Startup.cs
. Well, I could but it would ruin my project's architecture. The headless features are added in a separate assembly to the site's Core and Web code. The idea is that we can just drop the headless library into a baseline project to "switch on" headless functionality.
The Solution
Below is what is now working for me.
This composer lives in my BaseProject.Headless
library and adds the appropriate CORS, so long as a frontend URI is set.
public class Composer : IComposer
{
private const string FrontendCorsPolicyName = "FrontendCorsPolicy";
public void Compose(IUmbracoBuilder builder)
{
if (Uri.TryCreate(builder.Config.GetSection("Headless:FrontendUrl")?.Value, UriKind.Absolute, out Uri? frontendUri))
{
builder.Services.AddCors(options =>
{
options.AddPolicy(FrontendCorsPolicyName, corsPolicyBuilder =>
{
corsPolicyBuilder.WithOrigins(frontendUri.ToString())
.SetIsOriginAllowed(origin => new Uri(origin).Host == "localhost")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.Configure<UmbracoPipelineOptions>(options =>
{
options.AddFilter(new UmbracoPipelineFilter(FrontendCorsPolicyName)
{
PrePipeline = app =>
{
app.UseCors(FrontendCorsPolicyName);
},
});
});
}
}
}
Of special note is this:
corsPolicyBuilder.SetIsOriginAllowed(origin =>
new Uri(origin).Host == "localhost"
)
This is here because we can't guarantee devs running the frontend locally will be using any specific port (or scheme) - this should probably be wrapped in an environment check.
Top comments (0)