skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.

    • If using an App Service for hosting and nothing else, the proper way seems to be to define a CORS policy in the application code (e.g., Startup.cs) and disable CORS functionality in the App Service. App Services don't appear to offer the ability to configure this for some reason, and enabling CORS there overrides anything defined in the application code.
    • If using Azure API Management, as is the case for us, CORS must be configured as a policy in API Management (see docs here and here). This seems to override CORS policies set elsewhere, which is why we weren't having any luck trying to set it in the application code initially.
    • If the app is accessed through additional layers (like Azure Application Gateway) it's possible headers need to be configured there instead.

  2. 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:

    [HttpGet]
    [Route("getfile")]
    public async Task<ActionResult> GetFile(long id)
    {
        var file = await _fileService.GetFile(id);
        
        // Add the "Access-Control-Expose-Headers" header
        Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
    
        return File(file.Content, file.MimeType, file.Name);
    }
    
    • This line 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search