Búsqueda de sitios web

c# - No puedo acceder a Response.Headers("Content-Disposition") en el cliente incluso después de habilitar CORS


Estoy tratando de recuperar el nombre de un archivo de Excel que se genera en el backend (Asp.net Core 2.2) y, como puedes ver en el código C#, el nombre del archivo se recupera en el encabezado de la respuesta, pero desde el cliente no puedo acceder al encabezado 'Content Disposition'

Como puedes ver en la impresión abajo, a pesar de que la disposición de contenido no está presente en los encabezados, está presente en los encabezados de respuesta XHR

registro de response.headers

XHR

Ya habilité la política CORS en el backend como puedes ver aquí:

Startup.cs (método ConfigureServices)

services
    .AddCors(options =>
    {
        options.AddPolicy(CorsAllowAllOrigins,
            builder => builder.WithOrigins("*").WithHeaders("*").WithMethods("*"));
    })
    .AddMvc(options =>
    {
        options.Filters.Add(new AuthorizeFilter(authorizationPolicy));
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        var key = Encoding.ASCII.GetBytes(jwtSecurityOptions.SecretKey);

        options.Audience = jwtSecurityOptions.Audience;
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false
        };
    })

Startup.cs(método Configure)

app.UseAuthentication();
app.UseCors(CorsAllowAllOrigins);
app.UseMvc();

controladora

[HttpGet("{reportResultId}/exportExcelFile")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesErrorResponseType(typeof(void))]
[Authorize(Policy = nameof(RoleType.N1))]
public ActionResult ExportExcelFile(int reportResultId, [FromQuery]bool matchedRows, [FromQuery]ExportExcelType exportExcelType)
{
    var authenticatedUser = Request.GetAuthenticatedUser();
    var result = _reconciliationResultService.GetDataForExportExcelFile(reportResultId, matchedRows,authenticatedUser.UserName ,exportExcelType, authenticatedUser.UserName, authenticatedUser.Id);

    if (result != null)
    {
        MemoryStream memoryStream = new MemoryStream(result.WorkbookContent);
        var contentType = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        var fileStreamResult = new FileStreamResult(memoryStream, contentType)
        {
            FileDownloadName = result.FileName
        };

        fileStreamResult.FileDownloadName=result.FileName;

        return fileStreamResult;
    }
    else
    {
        return null;
    }
}

--------------Cliente ---------------------

Contenedor.ts

exportExcelFile(matchedRows: string) {
    this._reportService.exportExcelFile(matchedRows, this.reportInfo.id, this.exportExcelType).subscribe((response) => {
        var filename = response.headers.get("Content-Disposition").split('=')[1]; //An error is thrown in this line because response.headers.get("Content-Disposition") is always null
        filename = filename.replace(/"/g, "")
        const blob = new Blob([response.body],
            { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

        const file = new File([blob], filename,
            { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

        this.exportingExcelFile = false;
        this.ready = true;
        saveAs(file);
    });
}

ReportService.ts

exportExcelFile(matchedRows: string, reportInfoId: number, exportExcelType:ExportExcelType): Observable<any> {
    const url = `${environment.apiUrls.v1}/reconciliationResult/${reportInfoId}/exportExcelFile?matchedRows=${matchedRows}&exportExcelType=${exportExcelType}`;
    return this.http.get(url, { observe: 'response', responseType: 'blob' });
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { AppRoutingModule } from './app.routing.module';
import { ReportModule } from './report/report.module';

import { AppComponent } from './app.component';
import { TopNavComponent } from './layout/topnav.component';
import { SideBarComponent } from './layout/sidebar.component';
import { DatePipe } from '@angular/common';
import { DynamicDialogModule } from 'primeng/dynamicdialog';
import { SharedModule } from './core/shared.module';
import { JwtInterceptor } from './core/_helpers/jwt.interceptor';
import { ErrorInterceptor } from './core/_helpers/error.interceptor';
import { LoginRoutingModule } from './login/login-routing.module';
import { LogingModule } from './login/login.module';

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    SharedModule,
    ReportModule,
    AppRoutingModule,
    DynamicDialogModule,
    LogingModule,
    LoginRoutingModule
  ],
  declarations: [
    AppComponent,
    TopNavComponent,
    SideBarComponent
  ],
  providers: [
    DatePipe,
    { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
     { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})

export class AppModule { }
```

Note: I'm implementing JWT. I don't know if it can have any impact on the headers.

Respuesta #1

Tu servidor necesita exponer el encabezado como parte de su configuración CORS, usando WithExposedHeaders. Aquí tienes un ejemplo:

services
    .AddCors(options =>
    {
        options.AddPolicy(CorsAllowAllOrigins, builder => 
            builder.WithOrigins("*")
                   .WithHeaders("*")
                   .WithMethods("*")
                   .WithExposedHeaders("Content-Disposition"));
    });

Esto establece el encabezado Access-Control-Expose-Headers.

Artículos relacionados