I’m working on a web app which uses a .NET web API to download a file. The request to the API is handled by client code generated using NSwag. This all works great locally, but when using the API that’s hosted on Azure, the client code is unable to get the file name.
The client relies on the "Content-Disposition" response header to set the file name for the download. That header is automatically added by the API controller, which returns a FileContentResult. However, because "Content-Disposition" isn’t a CORS-safelisted response header, the client isn’t able to access it unless "Access-Control-Expose-Headers" is also provided to allow it.
Specifying this in the CORS policy in the API app’s startup works great locally, but it seems the WithExposedHeaders
setting isn’t respected when it’s hosted on Azure.
Looking in the browser dev tools the "Content-Disposition" header is there with the expected file name, but "Access-Control-Expose-Headers" is not. The same request has both headers as expected when using the local API, though.
What is the proper way to expose the "Content-Disposition" header to my client from the Azure App Service?
Details
Here’s a simplified version of the relevant startup code:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.WithExposedHeaders("Content-Disposition");
});
});
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
And a simplified version of the API controller:
[HttpGet]
[Route("getfile")]
public async Task<ActionResult> GetFile(long id)
{
var file = await _fileService.GetFile(id);
return File(file.Content, file.MimeType, file.Name);
}
I thought the issue might be that the CORS settings defined on Azure override the app-defined policy, but clearing out all of the Azure CORS settings had no effect.
I also tried the setting suggested in this answer with "Content-Disposition" as the value, but it didn’t appear to do anything. Not really sure that’s even a valid setting as I can’t find any other references to it besides that SO answer.
It seems like setting the exposed headers on Azure, as shown here, should be the answer but my App Service CORS config doesn’t have those options. Guessing that may only apply to Azure Container Apps.
2
Answers
Additional research eventually led us to a solution. Short answer: the proper way to expose the "Content-Disposition" header from an API hosted as an Azure App Service depends on how the app is being accessed through Azure.
One approach can consider is that setting the "Access-Control-Expose-Headers" header in the API controller action. This way, you can check that the header is included in the response regardless of CORS policies.
Controller.cs:
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
expose the "Content-Disposition" header in the response. This allows the client to access the header and extract the file name for the download.This is a commonly used approach to expose headers that are not included in the default CORS policy. I can’t directly test the code myself, there can be unexpected interactions or limitations in different hosting environments.