39 votes

ASP.NET Core CORS WebAPI : pas d'en-tête Access-Control-Allow-Origin

J'ai déployé mon API web ASP.NET Core sur Azure, et je peux accéder à ses points de terminaison à l'aide de Swagger ou d'un débogueur web comme Fiddler. Dans les deux cas (même origine dans Swagger, origine différente en utilisant Fiddler à partir de mon ordinateur), lorsque j'accède aux API, j'obtiens le résultat escompté, avec CORS activé comme suit dans mon fichier Startup.cs :

  1. ajouter services.AddCors(); a ConfigureServices .

  2. ajouter le middleware à Configure : Je suis conscient que l'ordre est important ici ( ASP.NET 5 : Access-Control-Allow-Origin dans la réponse ), je place donc cet appel au sommet de la méthode, précédé seulement par l'enregistrement ou le diagnostic du middleware ; voici ma méthode complète :

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IDatabaseInitializer databaseInitializer) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); loggerFactory.AddNLog();

    // to serve up index.html
    app.UseDefaultFiles();
    app.UseStaticFiles();
    
    // http://www.talkingdotnet.com/aspnet-core-diagnostics-middleware-error-handling/
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
    
    // CORS
    // https://docs.asp.net/en/latest/security/cors.html
    app.UseCors(builder =>
            builder.WithOrigins("http://localhost:4200", "http://www.myclientserver.com")
                .AllowAnyHeader()
                .AllowAnyMethod());
    
    app.UseOAuthValidation();
    app.UseOpenIddict();
    app.UseMvc();
    
    databaseInitializer.Seed().GetAwaiter().GetResult();
    env.ConfigureNLog("nlog.config");
    
    // swagger
    app.UseSwagger();
    app.UseSwaggerUi();

    }

En localhost CORS est utilisé pendant le développement et fait référence à une application Angular2 CLI. CORS fonctionne bien localement, et mes applications client et API sont sur des ports différents sur le même localhost, il s'agit donc d'une origine croisée "vraie" (je fais cette remarque en raison des suggestions que j'ai trouvées ici : https://weblog.west-wind.com/posts/2016/Sep/26/ASPNET-Core-and-CORS-Gotchas : l'auteur du message remarque que l'en-tête CORS dans la réponse est envoyé uniquement lorsque cela est réellement nécessaire, c'est-à-dire dans de véritables environnements inter-origines).

En utilisant Fiddler, je peux accéder avec succès à l'API distante, mais je n'obtiens aucun résultat. Access-Control-Allow-Origin en-tête. Ainsi, lorsque j'appelle l'API depuis le navigateur (par le biais de mon application client), la demande AJAX échoue, même si le serveur renvoie 200. Exemple de requête Fiddler (succès) :

GET http://mywebapisiteurl/api/values HTTP/1.1
User-Agent: Fiddler

réponse :

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=prinapi.azurewebsites.net
Date: Thu, 01 Dec 2016 10:30:19 GMT

["value1","value2"]

Lorsque j'essaie d'accéder à l'API distante déployée sur Azure, mon application cliente échoue toujours sa requête AJAX avec une erreur :

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.myclientserver.com' is therefore not allowed access.

Voici un exemple de code client utilisant Angular2 (en utilisant Plunker) :

import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import { Http, Headers, Response } from '@angular/http';
import { HttpModule } from '@angular/http';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <button (click)="test()">test</button>
    </div>
  `,
})
export class App {
  name:string;
  constructor(private _http: Http) {
    this.name = 'Angular2'
  }
  public test() {
    this._http.get('http://theapisiteurlhere/api/values',
    {
        headers: new Headers({
          'Content-Type': 'application/json'
        })
    })
    .subscribe(
      (data: any) => {
        console.log(data);
      },
      error => {
        console.log(error);
      });
  }
}

@NgModule({
  imports: [ BrowserModule, HttpModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

En résumé, il semble que le serveur de l'API ASPNET ne renvoie pas les en-têtes CORS attendus, et que mon client basé sur un navigateur hébergé sur une origine différente échoue donc. Pourtant, la configuration CORS semble correcte, du moins si l'on en croit la documentation citée plus haut ; je suis dans un véritable environnement multi-origine ; et je place l'intergiciel avant les autres. Peut-être que quelque chose d'évident m'échappe, mais en cherchant sur Google, je n'ai trouvé que ces recommandations. Un conseil ?

UPDATE

En réponse à @Daniel J.G : la demande/réponse de fiddler est réussie :

GET http://theapiserver/api/values HTTP/1.1
User-Agent: Fiddler
Host: theapiserver
Origin: http://theappserver/apps/prin

et :

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: http://theappserver/apps/prin
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=theapiserver
Date: Thu, 01 Dec 2016 14:15:21 GMT
Content-Length: 19

["value1","value2"]

La demande/réponse d'Angular2 (Plunker) échoue plutôt, comme signalé. En inspectant le trafic réseau, je peux voir la demande de contrôle préalable uniquement :

OPTIONS http://theapiserver/api/values HTTP/1.1
Host: theapiserver
Proxy-Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://run.plnkr.co
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://run.plnkr.co/h17wYofXGFuTy2Oh/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6

HTTP/1.1 204 No Content
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=3d551180c72208c1d997584c2b6119cf44e3a55c868f05ffc9258d25a58e95b1;Path=/;Domain=theapiserver
Date: Thu, 01 Dec 2016 14:23:02 GMT

Après cela, la demande échoue et plus aucun trafic n'est acheminé vers le serveur. Le problème signalé est le suivant Response to preflight request doesn't pass access control check à cause de l'absence de l'en-tête dans la réponse :

XMLHttpRequest cannot load http://theapiserver/api/values. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://run.plnkr.co' is therefore not allowed access.

0 votes

Avez-vous essayé de régler les en-têtes host et origin dans fiddler (host sur l'hôte de l'api et origin sur un autre pour simuler le cross origin) ? Cela pourrait également aider si vous postez les détails d'une requête faite à partir d'angular.

0 votes

Merci, j'ai mis à jour mon post (voir en bas). En bref, avec Fiddler, tout va bien ; avec Angular2, la demande de contrôle préalable échoue car il n'y a pas d'en-tête ACAO dans la réponse du serveur, bien que CORS soit activé.

0 votes

Vous êtes sûr de l'origine http://run.plnkr.co est l'une des origines autorisées ?

11voto

Naftis Points 808

Voici la réponse à ma propre question, copiée des commentaires : Je n'avais pas remarqué que dans Portail Azure il y a une section CORS. Si je n'y saisis aucune origine autorisée, ma configuration basée sur le code semble être totalement hors de propos. Cela me semble étrange, car je suis obligé de dupliquer les URL ici, mais une fois que j'ai ajouté * aux origines autorisées là ça a marché.

0 votes

Pour les lecteurs intéressés, il s'agit d'un nouvel échantillon minimal pour le flux de mots de passe utilisant OpenIdDict rc 3, qui a subi quelques changements d'API : github.com/Myrmex/oid-credentials . Faites attention au paramètre de l'attribut [Authorize] (note de fin dans le fichier readme).

0 votes

Je n'ai pas testé mais si c'est vrai, quel est l'intérêt d'avoir une configuration CORS dans le code ? merci Naftis de l'avoir signalé :)

2 votes

Vous venez de m'épargner des heures de dépannage ! Merci ! J'apprécie que MS autorise la configuration CORS dans le portail Azure, mais il est frustrant que ce ne soit pas plus évident. Cela semble complètement ignorer la configuration CORS basée sur le code (comme d'autres l'ont dit)...

4voto

Ashish Patel Points 71

L'ajout de la méthode .AllowAnyHeader() pourrait résoudre votre problème.

app.UseCors(builder => builder.WithOrigins("http://localhost:4200")
                              .AllowAnyMethod()
                              .AllowAnyHeader());

2voto

karan chavan Points 85

J'étais confronté à une erreur similaire, mais mon erreur a été résolue en gardant le pipeline en ordre. (startup.cs -> configureServices) comme

  app.UseRouting();
  app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
  app.UseEndpoints(endpoints => .....

1voto

Dumber_Texan2 Points 90

Le message d'erreur est très trompeur. J'obtenais la même erreur après avoir essayé d'ajouter une nouvelle table au DbContext. Angular ne donnait pas d'erreur "Access-Control-Allow-Origin", mais Postman donnait une erreur 500 Internal Server. La méthode de connexion échoue lorsqu'elle tente d'appeler _userManager.FindByEmailAsync(). J'espère que cela aidera quelqu'un d'autre.

-3voto

immirza Points 1926

Juste au cas où vous seriez paresseux de comprendre ce que la magie UseCors fait derrière la scène.

    // Summary:
    //     Adds a CORS middleware to your web application pipeline to allow cross domain
    //     requests.
    //
    // Parameters:
    //   app:
    //     The IApplicationBuilder passed to your Configure method.
    //
    //   configurePolicy:
    //     A delegate which can use a policy builder to build a policy.
    //
    // Returns:
    //     The original app parameter

Maintenant, vous pouvez utiliser en toute sécurité les lignes suivantes

    app.UseCors(builder =>
                    builder.WithOrigins("http://localhost:53593")
                        .AllowAnyOrigin()
                        .AllowAnyHeader()
                        .AllowAnyMethod());

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X