Événements
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plusCe navigateur n’est plus pris en charge.
Effectuez une mise à niveau vers Microsoft Edge pour tirer parti des dernières fonctionnalités, des mises à jour de sécurité et du support technique.
Notes
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
Par Rick Anderson et Kirk Larkin
Cet article montre comment Cross-Origin Resource Sharing (CORS) est activé dans une application ASP.NET Core.
La sécurité du navigateur empêche une page Web d’envoyer des requêtes à un domaine différent de celui qui a servi la page Web. Cette restriction est appelée stratégie de même origine. La politique de la même origine empêche un site malveillant de lire les données sensibles d'un autre site. Parfois, vous souhaiterez peut-être autoriser d’autres sites à effectuer des demandes cross-origin à votre application. Pour plus d’informations, consultez l’article Mozilla CORS.
CORS (Cross Origin Resource Sharing) :
Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports (RFC 6454) identiques.
Ces deux URL ont la même origine :
https://example.com/foo.html
https://example.com/bar.html
Ces URL ont des origines différentes des deux URL précédentes :
https://example.net
: Domaine différenthttps://contoso.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différentIl y a trois manières pour activer CORS :
L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.
Avertissement
UseCors doit être appelé dans le bon ordre. Pour plus d’informations, consultez Ordre des middlewares. Par exemple, UseCors
doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching
.
Chaque approche est détaillée dans les sections suivantes.
CORS Middleware gère les demandes cross-origin. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent :
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire._myAllowSpecificOrigins
stratégie CORS. UseCors
ajoute l’intergiciel CORS. L’appel à UseCors
doit être placé après UseRouting
, mais avant UseAuthorization
. Pour plus d’informations, consultez Ordre des middlewares.WithOrigins
, sont décrites plus loin dans cet article._myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage des points de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques.Avec le routage de point de terminaison, l’intergiciel CORS doit être configuré pour s’exécuter entre les appels à UseRouting
et UseEndpoints
.
L’appel de méthode AddCors ajoute des services CORS au conteneur de service de l’application :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour plus d’informations, consultez Options de stratégie CORS dans ce document.
Les méthodes CorsPolicyBuilder peuvent être chaînées, comme indiqué dans le code suivant :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Remarque : l’URL spécifiée ne doit pas contenir de barre oblique de fin (/
). Si l’URL se termine par /
, la comparaison retourne false
et aucun en-tête n’est retourné.
En règle générale, UseStaticFiles
est appelé avant UseCors
. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques entre sites doivent appeler UseCors
avant UseStaticFiles
.
Le code mis en surbrillance suivant active la stratégie CORS par défaut :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.
Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Dans le code précédent :
app.UseCors
active l’intergiciel CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors()
elle seule n’active pas CORS./echo
autorisent les demandes inter-origines à l’aide de la stratégie spécifiée./echo2
et Razor n’autorisent pas les demandes cross-origin, car aucune stratégie par défaut n’a été spécifiée.L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage de point de terminaison avec RequireCors
.
Consultez Tester CORS avec l’attribut [EnableCors] et méthode RequireCors pour obtenir des instructions sur le code de test similaire au précédent.
L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.
L’attribut [EnableCors] fournit une alternative à l’application globale de CORS. L’attribut [EnableCors]
active CORS pour les points de terminaison sélectionnés, plutôt que tous les points de terminaison :
[EnableCors]
spécifie la stratégie par défaut.[EnableCors("{Policy String}")]
spécifie une stratégie nommée.L’attribut [EnableCors]
peut s'appliquer aux éléments suivants :
PageModel
Différentes stratégies peuvent être appliquées aux contrôleurs, aux modèles de page ou aux méthodes d’action avec l’attribut [EnableCors]
. Lorsque l’attribut [EnableCors]
est appliqué à un contrôleur, à un modèle de page ou à une méthode d’action, et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le Utilisez l'attribut ou l'intergiciel [EnableCors]
, mais pas les deux dans la même application.
Le code suivant applique une stratégie différente à chaque méthode :
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Le code suivant crée deux stratégies CORS :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour le meilleur contrôle de la limitation des requêtes CORS :
[EnableCors("MyPolicy")]
avec une stratégie nommée.Le code de la section suivante répond à la liste précédente.
L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.
Le code suivant définit la stratégie CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Le code suivant désactive CORS pour l’action GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Le code précédent :
"MyPolicy"
stratégie CORS pour le contrôleur.GetValues2
.Pour obtenir des instructions sur le test du code précédent, consultez Test CORS.
Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :
AddPolicy est appelé dans Program.cs
. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS.
AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http
ou https
). AllowAnyOrigin
n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.
Notes
La spécification de AllowAnyOrigin
et AllowCredentials
est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.
AllowAnyOrigin
affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.
SetIsOriginAllowedToAllowWildcardSubdomains: définit la propriété IsOriginAllowed de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.
Une politique CORS Middleware correspondant à des en-têtes spécifiques spécifiés par WithHeaders
n'est possible que si les en-têtes envoyés dans Access-Control-Request-Headers
correspondent exactement aux en-têtes indiqués dans WithHeaders
.
Pour instance, envisagez une application configurée comme suit :
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware refuse une demande préliminaire avec l’en-tête de demande suivant, car Content-Language
(HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L’application retourne une réponse 200 OK, mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.
Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.
Les en-têtes de réponse disponibles par défaut sont les suivants :
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut le navigateur n’envoie pas d’informations d’identification avec une requête de cross-origin. Les informations d’identification incluent des cookies et des schémas d’authentification HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials
sur true
.
En utilisant XMLHttpRequest
directement :
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Utilisation de jQuery :
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Utilisation de l’API Fetch :
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La réponse HTTP inclut un Access-Control-Allow-Credentials
en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.
Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials
, le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.
L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.
La spécification CORS indique également que la définition des origines sur "*"
(toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials
est présent.
Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutesles conditions suivantes sont remplies :
Accept
, Accept-Language
, Content-Language
, Content-Type
ou Last-Event-ID
.Content-Type
, s'il est défini, a l'une des valeurs suivantes : application/x-www-form-urlencoded
multipart/form-data
text/plain
La règle sur les en-têtes de demande définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader
sur l’objet XMLHttpRequest
. La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent
, Host
ou Content-Length
.
Notes
Cet article contient des URL créées en déployant l’exemple de code sur deux sites web Azure, https://cors3.azurewebsites.net
et https://cors.azurewebsites.net
.
Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
La requête préliminaire utilise la méthode HTTP OPTIONS. Il peut inclure les en-têtes suivants :
User-Agent
.Si la demande préliminaire est refusée, l’application retourne une réponse 200 OK
, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.
À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, en fonction du navigateur :
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motif : la demande CORS n’a pas réussi). En savoir plusPour autoriser des en-têtes spécifiques, appelez WithHeaders :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers
. Si :
"*"
Accept
, Content-Type
et Origin
, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.Lorsque la stratégie CORS est appliquée :
app.UseCors
dans Program.cs
.[EnableCors]
.ASP.NET Core répond à la demande d’OPTIONS en pré-vol.
La section Test CORS de ce document illustre ce comportement.
Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS.
Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Consultez Tester CORS avec l’attribut [EnableCors] et méthode RequireCors pour obtenir des instructions sur le test de code précédent.
L’en-tête Access-Control-Max-Age
spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.
<script>
pour recevoir la réponse. Les scripts sont autorisés à être chargés entre origines.La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.
Voici un exemple de demande d’origine croisée du bouton de test Valeurs vers https://cors1.azurewebsites.net/api/values
. En-tête Origin
:
En-têtes généraux
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
En-têtes de réponse
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Dans les requêtes OPTIONS
, le serveur définit l’en-tête Access-Control-Allow-Origin: {allowed origin}
des en-têtes de réponse dans la réponse. Par exemple, dans l’exemple de code, la demande OPTIONS
du bouton Delete [EnableCors]
contient les en-têtes suivants :
En-têtes généraux
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
En-têtes de réponse
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La valeur https://cors1.azurewebsites.net
de cet en-tête correspond à l’en-tête Origin
de la demande.
Si AllowAnyOrigin est appelé,Access-Control-Allow-Origin: *
, la valeur de caractère générique est retournée. AllowAnyOrigin
autorise n’importe quelle origine.
Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin
, la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.
Les demandes adressées à un point de terminaison à l’aide de HTTP qui sont redirigées vers HTTPS échouent UseHttpsRedirection avec ERR_INVALID_REDIRECT on the CORS preflight request
.
Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection
pour rediriger les requêtes vers HTTPS.
Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.
L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avertissement
WithOrigins("https://localhost:<port>");
doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.
ValuesController
Voici les points de terminaison pour les tests :
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :
dotnet run
à l’aide de l’URL par défaut de https://localhost:5001
.https://localhost:44398
.Utilisation d’un navigateur avec les outils F12 :
Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau.
Sélectionnez le bouton test PUT. Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.
Sélectionnez le bouton GetValues2 [DisableCors]
pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur similaire à celle indiquée ci-après s'affiche :
L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2'
partir de l’origine 'https://cors3.azurewebsites.net'
a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.
Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl ou Fiddler. Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l’en-tête Origin
doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin
:
La commande suivante utilise curl
pour émettre une demande OPTIONS avec des informations :
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS par point de terminaison à l’aide de RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Notez que seul le point de terminaison /echo
utilise le RequireCors
pour autoriser les demandes cross-origin à l’aide de la stratégie spécifiée. Les contrôleurs ci-dessous activent CORS à l’aide de l’attribut [EnableCors].
Les éléments suivants TodoItems1Controller
fournissent des points de terminaison pour les tests :
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id) {
if (id < 1) {
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors]
et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :
headers: {
"Content-Type": "x-custom-header"
},
Les éléments suivants TodoItems2Controller
fournissent des points de terminaison similaires, mais incluent du code explicite pour répondre aux demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Le code précédent peut être testé en déployant l’exemple sur Azure. Dans la liste déroulante Contrôleur, sélectionnez Contrôle préliminaire, puis Définir le contrôleur. Tous les appels CORS aux points de terminaison TodoItems2Controller
réussissent.
Par Rick Anderson et Kirk Larkin
Cet article explique comment activer CORS dans une application ASP.NET Core.
La sécurité du navigateur empêche une page Web d’envoyer des requêtes à un domaine différent de celui qui a servi la page Web. Cette restriction est appelée stratégie de même origine. La politique de la même origine empêche un site malveillant de lire les données sensibles d'un autre site. Parfois, vous souhaiterez peut-être autoriser d’autres sites à effectuer des demandes cross-origin à votre application. Pour plus d’informations, consultez l’article Mozilla CORS.
CORS (Cross Origin Resource Sharing) :
Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports (RFC 6454) identiques.
Ces deux URL ont la même origine :
https://example.com/foo.html
https://example.com/bar.html
Ces URL ont des origines différentes des deux URL précédentes :
https://example.net
: Domaine différenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différentIl y a trois manières pour activer CORS :
L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.
Avertissement
UseCors doit être appelé dans le bon ordre. Pour plus d’informations, consultez Ordre des middlewares. Par exemple, UseCors
doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching
.
Chaque approche est détaillée dans les sections suivantes.
CORS Middleware gère les demandes cross-origin. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent :
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire._myAllowSpecificOrigins
stratégie CORS. UseCors
ajoute l’intergiciel CORS. L’appel à UseCors
doit être placé après UseRouting
, mais avant UseAuthorization
. Pour plus d’informations, consultez Ordre des middlewares.WithOrigins
, sont décrites plus loin dans cet article._myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage des points de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques.Avec le routage de point de terminaison, l’intergiciel CORS doit être configuré pour s’exécuter entre les appels à UseRouting
et UseEndpoints
.
L’appel de méthode AddCors ajoute des services CORS au conteneur de service de l’application :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour plus d’informations, consultez Options de stratégie CORS dans ce document.
Les méthodes CorsPolicyBuilder peuvent être chaînées, comme indiqué dans le code suivant :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Remarque : l’URL spécifiée ne doit pas contenir de barre oblique de fin (/
). Si l’URL se termine par /
, la comparaison retourne false
et aucun en-tête n’est retourné.
Avertissement
UseCors
doit être placé après UseRouting
et avant UseAuthorization
. Cela permet de s’assurer que les en-têtes CORS sont inclus dans la réponse pour les appels autorisés et non autorisés.
En règle générale, UseStaticFiles
est appelé avant UseCors
. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques entre sites doivent appeler UseCors
avant UseStaticFiles
.
Le code mis en surbrillance suivant active la stratégie CORS par défaut :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.
Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Dans le code précédent :
app.UseCors
active l’intergiciel CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors()
elle seule n’active pas CORS./echo
autorisent les demandes inter-origines à l’aide de la stratégie spécifiée./echo2
et Razor n’autorisent pas les demandes cross-origin, car aucune stratégie par défaut n’a été spécifiée.L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage de point de terminaison avec RequireCors
.
Dans ASP.NET Core 7.0, l’attribut [EnableCors]
doit passer un paramètre ou un avertissement ASP0023 est généré à partir d’une correspondance ambiguë sur l’itinéraire. ASP.NET Core 8.0 et versions ultérieures ne génère pas l’avertissementASP0023
.
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Consultez Tester CORS avec l’attribut [EnableCors] et méthode RequireCors pour obtenir des instructions sur le code de test similaire au précédent.
L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.
L’attribut [EnableCors] fournit une alternative à l’application globale de CORS. L’attribut [EnableCors]
active CORS pour les points de terminaison sélectionnés, plutôt que tous les points de terminaison :
[EnableCors]
spécifie la stratégie par défaut.[EnableCors("{Policy String}")]
spécifie une stratégie nommée.L’attribut [EnableCors]
peut s'appliquer aux éléments suivants :
PageModel
Différentes stratégies peuvent être appliquées aux contrôleurs, aux modèles de page ou aux méthodes d’action avec l’attribut [EnableCors]
. Lorsque l’attribut [EnableCors]
est appliqué à un contrôleur, à un modèle de page ou à une méthode d’action, et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le Utilisez l'attribut ou l'intergiciel [EnableCors]
, mais pas les deux dans la même application.
Le code suivant applique une stratégie différente à chaque méthode :
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Le code suivant crée deux stratégies CORS :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour le meilleur contrôle de la limitation des requêtes CORS :
[EnableCors("MyPolicy")]
avec une stratégie nommée.Le code de la section suivante répond à la liste précédente.
L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.
Le code suivant définit la stratégie CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Le code suivant désactive CORS pour l’action GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Le code précédent :
"MyPolicy"
stratégie CORS pour le contrôleur.GetValues2
.Pour obtenir des instructions sur le test du code précédent, consultez Test CORS.
Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :
AddPolicy est appelé dans Program.cs
. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS.
AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http
ou https
). AllowAnyOrigin
n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.
Notes
La spécification de AllowAnyOrigin
et AllowCredentials
est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.
AllowAnyOrigin
affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.
SetIsOriginAllowedToAllowWildcardSubdomains: définit la propriété IsOriginAllowed de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.
Une politique CORS Middleware correspondant à des en-têtes spécifiques spécifiés par WithHeaders
n'est possible que si les en-têtes envoyés dans Access-Control-Request-Headers
correspondent exactement aux en-têtes indiqués dans WithHeaders
.
Pour instance, envisagez une application configurée comme suit :
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware refuse une demande préliminaire avec l’en-tête de demande suivant, car Content-Language
(HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L’application retourne une réponse 200 OK, mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.
Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.
Les en-têtes de réponse disponibles par défaut sont les suivants :
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut le navigateur n’envoie pas d’informations d’identification avec une requête de cross-origin. Les informations d’identification incluent des cookies et des schémas d’authentification HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials
sur true
.
En utilisant XMLHttpRequest
directement :
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Utilisation de jQuery :
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Utilisation de l’API Fetch :
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La réponse HTTP inclut un Access-Control-Allow-Credentials
en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.
Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials
, le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.
L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.
La spécification CORS indique également que la définition des origines sur "*"
(toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials
est présent.
Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutesles conditions suivantes sont remplies :
Accept
, Accept-Language
, Content-Language
, Content-Type
ou Last-Event-ID
.Content-Type
, s'il est défini, a l'une des valeurs suivantes : application/x-www-form-urlencoded
multipart/form-data
text/plain
La règle sur les en-têtes de demande définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader
sur l’objet XMLHttpRequest
. La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent
, Host
ou Content-Length
.
Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
La requête préliminaire utilise la méthode HTTP OPTIONS. Il peut inclure les en-têtes suivants :
User-Agent
.Si la demande préliminaire est refusée, l’application retourne une réponse 200 OK
, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.
À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, en fonction du navigateur :
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motif : la demande CORS n’a pas réussi). En savoir plusPour autoriser des en-têtes spécifiques, appelez WithHeaders :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers
. Si :
"*"
Accept
, Content-Type
et Origin
, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.Lorsque la stratégie CORS est appliquée :
app.UseCors
dans Program.cs
.[EnableCors]
.ASP.NET Core répond à la demande d’OPTIONS en pré-vol.
La section Test CORS de ce document illustre ce comportement.
Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS.
Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Consultez Tester CORS avec l’attribut [EnableCors] et méthode RequireCors pour obtenir des instructions sur le test de code précédent.
L’en-tête Access-Control-Max-Age
spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.
<script>
pour recevoir la réponse. Les scripts sont autorisés à être chargés entre origines.La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.
Sélectionnez le bouton de test PUT sur l’exemple déployé.
En-tête Origin
:
En-têtes généraux
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
En-têtes de réponse
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Dans les requêtes OPTIONS
, le serveur définit l’en-tête Access-Control-Allow-Origin: {allowed origin}
des en-têtes de réponse dans la réponse. Par exemple, dans l’exemple de code, la demande OPTIONS
du bouton Delete [EnableCors]
contient les en-têtes suivants :
En-têtes généraux
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
En-têtes de réponse
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La valeur https://cors1.azurewebsites.net
de cet en-tête correspond à l’en-tête Origin
de la demande.
Si AllowAnyOrigin est appelé,Access-Control-Allow-Origin: *
, la valeur de caractère générique est retournée. AllowAnyOrigin
autorise n’importe quelle origine.
Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin
, la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.
Les demandes adressées à un point de terminaison à l’aide de HTTP qui sont redirigées vers HTTPS échouent UseHttpsRedirection avec ERR_INVALID_REDIRECT on the CORS preflight request
.
Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection
pour rediriger les requêtes vers HTTPS.
Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.
L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avertissement
WithOrigins("https://localhost:<port>");
doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.
ValuesController
Voici les points de terminaison pour les tests :
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :
dotnet run
à l’aide de l’URL par défaut de https://localhost:5001
.https://localhost:44398
.Utilisation d’un navigateur avec les outils F12 :
Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau.
Sélectionnez le bouton test PUT. Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.
Sélectionnez le bouton GetValues2 [DisableCors]
pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur similaire à celle indiquée ci-après s'affiche :
L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2'
partir de l’origine 'https://cors3.azurewebsites.net'
a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.
Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl ou Fiddler. Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l’en-tête Origin
doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin
:
La commande suivante utilise curl
pour émettre une demande OPTIONS avec des informations :
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS par point de terminaison à l’aide de RequireCors
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors("MyPolicy");
endpoints.MapControllers();
endpoints.MapRazorPages();
});
app.Run();
Notez que seul le point de terminaison /echo
utilise le RequireCors
pour autoriser les demandes cross-origin à l’aide de la stratégie spécifiée. Les contrôleurs ci-dessous activent CORS à l’aide de l’attribut [EnableCors].
Les éléments suivants TodoItems1Controller
fournissent des points de terminaison pour les tests :
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id) {
if (id < 1) {
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors("MyPolicy")]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors]
et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :
headers: {
"Content-Type": "x-custom-header"
},
Les éléments suivants TodoItems2Controller
fournissent des points de terminaison similaires, mais incluent du code explicite pour répondre aux demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided.
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// [EnableCors] // Warning ASP0023 Route '{id}' conflicts with another action route.
// An HTTP request that matches multiple routes results in an ambiguous
// match error.
[EnableCors("MyPolicy")] // Required for this path.
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors("MyPolicy")] // Required for this path.
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Le code précédent peut être testé en déployant l’exemple sur Azure. Dans la liste déroulante Contrôleur, sélectionnez Préliminaire, puis Définir le contrôleur. Tous les appels CORS aux points de terminaison TodoItems2Controller
réussissent.
Par Rick Anderson et Kirk Larkin
Cet article explique comment activer CORS dans une application ASP.NET Core.
La sécurité du navigateur empêche une page Web d’envoyer des requêtes à un domaine différent de celui qui a servi la page Web. Cette restriction est appelée stratégie de même origine. La politique de la même origine empêche un site malveillant de lire les données sensibles d'un autre site. Parfois, vous souhaiterez peut-être autoriser d’autres sites à effectuer des demandes cross-origin à votre application. Pour plus d’informations, consultez l’article Mozilla CORS.
CORS (Cross Origin Resource Sharing) :
Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports (RFC 6454) identiques.
Ces deux URL ont la même origine :
https://example.com/foo.html
https://example.com/bar.html
Ces URL ont des origines différentes des deux URL précédentes :
https://example.net
: Domaine différenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différentIl y a trois manières pour activer CORS :
L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.
Avertissement
UseCors doit être appelé dans le bon ordre. Pour plus d’informations, consultez Ordre des middlewares. Par exemple, UseCors
doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching
.
Chaque approche est détaillée dans les sections suivantes.
CORS Middleware gère les demandes cross-origin. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent :
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire._myAllowSpecificOrigins
stratégie CORS. UseCors
ajoute l’intergiciel CORS. L’appel à UseCors
doit être placé après UseRouting
, mais avant UseAuthorization
. Pour plus d’informations, consultez Ordre des middlewares.WithOrigins
, sont décrites plus loin dans cet article._myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage des points de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques.Avec le routage de point de terminaison, l’intergiciel CORS doit être configuré pour s’exécuter entre les appels à UseRouting
et UseEndpoints
.
L’appel de méthode AddCors ajoute des services CORS au conteneur de service de l’application :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour plus d’informations, consultez Options de stratégie CORS dans ce document.
Les méthodes CorsPolicyBuilder peuvent être chaînées, comme indiqué dans le code suivant :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Remarque : l’URL spécifiée ne doit pas contenir de barre oblique de fin (/
). Si l’URL se termine par /
, la comparaison retourne false
et aucun en-tête n’est retourné.
Avertissement
UseCors
doit être placé après UseRouting
et avant UseAuthorization
. Cela permet de s’assurer que les en-têtes CORS sont inclus dans la réponse pour les appels autorisés et non autorisés.
En règle générale, UseStaticFiles
est appelé avant UseCors
. Les applications qui utilisent JavaScript pour récupérer des fichiers statiques entre sites doivent appeler UseCors
avant UseStaticFiles
.
Le code mis en surbrillance suivant active la stratégie CORS par défaut :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.
L’activation de CORS par point de terminaison en utilisant RequireCors
ne prend pas en charge les demandes automatiques de contrôle en amont. Pour plus d’informations, consultez ce problème GitHub et testez CORS avec le routage du point de terminaison et [HttpOptions].
Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
app.Run();
Dans le code précédent :
app.UseCors
active l’intergiciel CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors()
elle seule n’active pas CORS./echo
autorisent les demandes inter-origines à l’aide de la stratégie spécifiée./echo2
et Razor n’autorisent pas les demandes cross-origin, car aucune stratégie par défaut n’a été spécifiée.L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage de point de terminaison avec RequireCors
.
Pour obtenir des instructions sur le test du code similaire au précédent, consultez Test CORS avec routage de point de terminaison et [HttpOptions].
L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.
L’attribut [EnableCors] fournit une alternative à l’application globale de CORS. L’attribut [EnableCors]
active CORS pour les points de terminaison sélectionnés, plutôt que tous les points de terminaison :
[EnableCors]
spécifie la stratégie par défaut.[EnableCors("{Policy String}")]
spécifie une stratégie nommée.L’attribut [EnableCors]
peut s'appliquer aux éléments suivants :
PageModel
Différentes stratégies peuvent être appliquées aux contrôleurs, aux modèles de page ou aux méthodes d’action avec l’attribut [EnableCors]
. Lorsque l’attribut [EnableCors]
est appliqué à un contrôleur, à un modèle de page ou à une méthode d’action, et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le Utilisez l'attribut ou l'intergiciel [EnableCors]
, mais pas les deux dans la même application.
Le code suivant applique une stratégie différente à chaque méthode :
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Le code suivant crée deux stratégies CORS :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
Pour le meilleur contrôle de la limitation des requêtes CORS :
[EnableCors("MyPolicy")]
avec une stratégie nommée.Le code de la section suivante répond à la liste précédente.
L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.
Le code suivant définit la stratégie CORS "MyPolicy"
:
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Le code suivant désactive CORS pour l’action GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Le code précédent :
"MyPolicy"
stratégie CORS pour le contrôleur.GetValues2
.Pour obtenir des instructions sur le test du code précédent, consultez Test CORS.
Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :
AddPolicy est appelé dans Program.cs
. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS.
AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http
ou https
). AllowAnyOrigin
n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.
Notes
La spécification de AllowAnyOrigin
et AllowCredentials
est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.
AllowAnyOrigin
affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.
SetIsOriginAllowedToAllowWildcardSubdomains: définit la propriété IsOriginAllowed de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Access-Control-Allow-Methods
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :
using Microsoft.Net.Http.Headers;
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
var MyAllowSpecificOrigins = "_MyAllowSubdomainPolicy";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
AllowAnyHeader
affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.
Une politique CORS Middleware correspondant à des en-têtes spécifiques spécifiés par WithHeaders
n'est possible que si les en-têtes envoyés dans Access-Control-Request-Headers
correspondent exactement aux en-têtes indiqués dans WithHeaders
.
Pour instance, envisagez une application configurée comme suit :
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware refuse une demande préliminaire avec l’en-tête de demande suivant, car Content-Language
(HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L’application retourne une réponse 200 OK, mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.
Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.
Les en-têtes de réponse disponibles par défaut sont les suivants :
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut le navigateur n’envoie pas d’informations d’identification avec une requête de cross-origin. Les informations d’identification incluent des cookies et des schémas d’authentification HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials
sur true
.
En utilisant XMLHttpRequest
directement :
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Utilisation de jQuery :
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Utilisation de l’API Fetch :
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
});
builder.Services.AddControllers();
var app = builder.Build();
La réponse HTTP inclut un Access-Control-Allow-Credentials
en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.
Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials
, le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.
L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.
La spécification CORS indique également que la définition des origines sur "*"
(toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials
est présent.
Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutesles conditions suivantes sont remplies :
Accept
, Accept-Language
, Content-Language
, Content-Type
ou Last-Event-ID
.Content-Type
, s'il est défini, a l'une des valeurs suivantes : application/x-www-form-urlencoded
multipart/form-data
text/plain
La règle sur les en-têtes de demande définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader
sur l’objet XMLHttpRequest
. La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent
, Host
ou Content-Length
.
Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
La requête préliminaire utilise la méthode HTTP OPTIONS. Il peut inclure les en-têtes suivants :
User-Agent
.Si la demande préliminaire est refusée, l’application retourne une réponse 200 OK
, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.
À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, en fonction du navigateur :
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motif : la demande CORS n’a pas réussi). En savoir plusPour autoriser des en-têtes spécifiques, appelez WithHeaders :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
});
builder.Services.AddControllers();
var app = builder.Build();
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
});
builder.Services.AddControllers();
var app = builder.Build();
Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers
. Si :
"*"
Accept
, Content-Type
et Origin
, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.Lorsque la stratégie CORS est appliquée :
app.UseCors
dans Program.cs
.[EnableCors]
.ASP.NET Core répond à la demande d’OPTIONS en pré-vol.
L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors
ne prend actuellement pas en charge les demandes de contrôle préliminaire automatique.
La section Test CORS de ce document illustre ce comportement.
Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS. Dans certains scénarios, cela peut ne pas être le cas. Par exemple, l’utilisation de CORS avec le routage de point de terminaison.
Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Pour obtenir des instructions sur le test du code précédent, consultez Test CORS avec routage de point de terminaison et [HttpOptions ].
L’en-tête Access-Control-Max-Age
spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});
builder.Services.AddControllers();
var app = builder.Build();
Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.
<script>
pour recevoir la réponse. Les scripts sont autorisés à être chargés entre origines.La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.
Voici un exemple de demande d’origine croisée du bouton de test Valeurs vers https://cors1.azurewebsites.net/api/values
. En-tête Origin
:
En-têtes généraux
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
En-têtes de réponse
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Dans les requêtes OPTIONS
, le serveur définit l’en-tête Access-Control-Allow-Origin: {allowed origin}
des en-têtes de réponse dans la réponse. Par exemple, l’exemple déployé, le Supprimer [EnableCors]boutonOPTIONS
contient les en-têtes suivants :
En-têtes généraux
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
En-têtes de réponse
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La valeur https://cors1.azurewebsites.net
de cet en-tête correspond à l’en-tête Origin
de la demande.
Si AllowAnyOrigin est appelé,Access-Control-Allow-Origin: *
, la valeur de caractère générique est retournée. AllowAnyOrigin
autorise n’importe quelle origine.
Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin
, la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.
Les demandes adressées à un point de terminaison à l’aide de HTTP qui sont redirigées vers HTTPS échouent UseHttpsRedirection avec ERR_INVALID_REDIRECT on the CORS preflight request
.
Les projets d’API peuvent rejeter les requêtes HTTP au lieu de les utiliser UseHttpsRedirection
pour rediriger les requêtes vers HTTPS.
Par défaut, les navigateurs Chrome et Edge n’affichent pas les requêtes OPTIONS sous l’onglet réseau des outils F12. Pour afficher les demandes OPTIONS dans ces navigateurs :
chrome://flags/#out-of-blink-cors
ou edge://flags/#out-of-blink-cors
Firefox affiche les demandes OPTIONS par défaut.
Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.
L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Avertissement
WithOrigins("https://localhost:<port>");
doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.
ValuesController
Voici les points de terminaison pour les tests :
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :
dotnet run
à l’aide de l’URL par défaut de https://localhost:5001
.https://localhost:44398
.Utilisation d’un navigateur avec les outils F12 :
Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau.
Sélectionnez le bouton test PUT. Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.
Sélectionnez le bouton GetValues2 [DisableCors]
pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur similaire à celle indiquée ci-après s'affiche :
L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2'
partir de l’origine 'https://cors3.azurewebsites.net'
a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.
Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl ou Fiddler. Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l’en-tête Origin
doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin
:
La commande suivante utilise curl
pour émettre une demande OPTIONS avec des informations :
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors
ne prend actuellement pas en charge les demandes de contrôle préliminaire automatique. Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
app.Run();
Les éléments suivants TodoItems1Controller
fournissent des points de terminaison pour les tests :
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Testez le code précédent à partir de la page de test (https://cors1.azurewebsites.net/test?number=1
) de l’exemple déployé.
Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors]
et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :
headers: {
"Content-Type": "x-custom-header"
},
Les éléments suivants TodoItems2Controller
fournissent des points de terminaison similaires, mais incluent du code explicite pour répondre aux demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Le code précédent peut être testé en déployant l’exemple sur Azure. Dans la liste déroulante Contrôleur, sélectionnez Préliminaire, puis Définir le contrôleur. Tous les appels CORS aux points de terminaison TodoItems2Controller
réussissent.
Par Rick Anderson et Kirk Larkin
Cet article explique comment activer CORS dans une application ASP.NET Core.
La sécurité du navigateur empêche une page Web d’envoyer des requêtes à un domaine différent de celui qui a servi la page Web. Cette restriction est appelée stratégie de même origine. La politique de la même origine empêche un site malveillant de lire les données sensibles d'un autre site. Parfois, vous souhaiterez peut-être autoriser d’autres sites à effectuer des demandes cross-origin à votre application. Pour plus d’informations, consultez l’article Mozilla CORS.
CORS (Cross Origin Resource Sharing) :
Affichez ou téléchargez l’exemple de code (procédure de téléchargement)
Deux URL ont la même origine si elles ont des schémas, des hôtes et des ports (RFC 6454) identiques.
Ces deux URL ont la même origine :
https://example.com/foo.html
https://example.com/bar.html
Ces URL ont des origines différentes des deux URL précédentes :
https://example.net
: Domaine différenthttps://www.example.com/foo.html
: Sous-domaine différenthttp://example.com/foo.html
: Schéma différenthttps://example.com:9000/foo.html
: Port différentIl y a trois manières pour activer CORS :
L’utilisation de l’attribut [EnableCors] avec une stratégie nommée fournit le meilleur contrôle pour limiter les points de terminaison qui prennent en charge CORS.
Avertissement
UseCors doit être appelé dans le bon ordre. Pour plus d’informations, consultez Ordre des middlewares. Par exemple, UseCors
doit être appelé avant UseResponseCaching lors de l’utilisation de UseResponseCaching
.
Chaque approche est détaillée dans les sections suivantes.
CORS Middleware gère les demandes cross-origin. Le code suivant applique une stratégie CORS à tous les points de terminaison de l’application avec les origines spécifiées :
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
// app.UseResponseCaching();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Le code précédent :
_myAllowSpecificOrigins
. Le nom de la stratégie est arbitraire._myAllowSpecificOrigins
stratégie CORS. UseCors
ajoute l’intergiciel CORS. L’appel à UseCors
doit être placé après UseRouting
, mais avant UseAuthorization
. Pour plus d’informations, consultez Ordre des middlewares.WithOrigins
, sont décrites plus loin dans cet article._myAllowSpecificOrigins
CORS pour tous les points de terminaison de contrôleur. Consultez Routage des points de terminaison pour appliquer une stratégie CORS à des points de terminaison spécifiques.Avec le routage de point de terminaison, l’intergiciel CORS doit être configuré pour s’exécuter entre les appels à UseRouting
et UseEndpoints
.
Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.
L’appel de méthode AddCors ajoute des services CORS au conteneur de service de l’application :
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
Pour plus d’informations, consultez Options de stratégie CORS dans ce document.
Les méthodes CorsPolicyBuilder peuvent être chaînées, comme indiqué dans le code suivant :
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
Remarque : l’URL spécifiée ne doit pas contenir de barre oblique de fin (/
). Si l’URL se termine par /
, la comparaison retourne false
et aucun en-tête n’est retourné.
Le code mis en surbrillance suivant active la stratégie CORS par défaut :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Le code précédent applique la stratégie CORS par défaut à tous les points de terminaison du contrôleur.
L’activation de CORS par point de terminaison en utilisant RequireCors
ne prend pas en charge les demandes automatiques de contrôle en amont. Pour plus d’informations, consultez ce problème GitHub et testez CORS avec le routage du point de terminaison et [HttpOptions].
Avec le routage de point de terminaison, CORS peut être activé par point de terminaison à l’aide de l’ensemble RequireCors de méthodes d’extension :
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/echo",
context => context.Response.WriteAsync("echo"))
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapControllers()
.RequireCors(MyAllowSpecificOrigins);
endpoints.MapGet("/echo2",
context => context.Response.WriteAsync("echo2"));
endpoints.MapRazorPages();
});
}
}
Dans le code précédent :
app.UseCors
active l’intergiciel CORS. Étant donné qu’aucune stratégie par défaut n’a été configurée, app.UseCors()
elle seule n’active pas CORS./echo
autorisent les demandes inter-origines à l’aide de la stratégie spécifiée./echo2
et Razor n’autorisent pas les demandes cross-origin, car aucune stratégie par défaut n’a été spécifiée.L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage de point de terminaison avec RequireCors
.
Pour obtenir des instructions sur le test du code similaire au précédent, consultez Test CORS avec routage de point de terminaison et [HttpOptions].
L’activation de CORS avec l’attribut [EnableCors] et l’application d’une stratégie nommée uniquement aux points de terminaison qui nécessitent CORS fournissent le meilleur contrôle.
L’attribut [EnableCors] fournit une alternative à l’application globale de CORS. L’attribut [EnableCors]
active CORS pour les points de terminaison sélectionnés, plutôt que tous les points de terminaison :
[EnableCors]
spécifie la stratégie par défaut.[EnableCors("{Policy String}")]
spécifie une stratégie nommée.L’attribut [EnableCors]
peut s'appliquer aux éléments suivants :
PageModel
Différentes stratégies peuvent être appliquées aux contrôleurs, aux modèles de page ou aux méthodes d’action avec l’attribut [EnableCors]
. Lorsque l’attribut [EnableCors]
est appliqué à un contrôleur, à un modèle de page ou à une méthode d’action, et que CORS est activé dans l’intergiciel, les deux stratégies sont appliquées. Nous vous déconseillons de combiner des stratégies. Utilisez le Utilisez l'attribut ou l'intergiciel [EnableCors]
, mais pas les deux dans la même application.
Le code suivant applique une stratégie différente à chaque méthode :
[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors("AnotherPolicy")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "green widget", "red widget" };
}
// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return id switch
{
1 => "green widget",
2 => "red widget",
_ => NotFound(),
};
}
}
Le code suivant crée deux stratégies CORS :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Policy1",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
options.AddPolicy("AnotherPolicy",
policy =>
{
policy.WithOrigins("http://www.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Pour le meilleur contrôle de la limitation des requêtes CORS :
[EnableCors("MyPolicy")]
avec une stratégie nommée.Le code de la section suivante répond à la liste précédente.
Consultez Test CORS pour obtenir des instructions sur le test du code similaire au code précédent.
L’attribut [DisableCors] ne désactive pas CORS qui a été activé par le routage du point de terminaison.
Le code suivant définit la stratégie CORS "MyPolicy"
:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
Le code suivant désactive CORS pour l’action GetValues2
:
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
Le code précédent :
"MyPolicy"
stratégie CORS pour le contrôleur.GetValues2
.Pour obtenir des instructions sur le test du code précédent, consultez Test CORS.
Cette section décrit les différentes options qui peuvent être définies dans une stratégie CORS :
AddPolicy est appelé dans Startup.ConfigureServices
. Pour certaines options, il peut être utile de lire d’abord la section Fonctionnement de CORS.
AllowAnyOrigin: autorise les requêtes CORS de toutes les origines avec n’importe quel schéma (http
ou https
). AllowAnyOrigin
n’est pas sécurisé, car n’importe quel site web peut effectuer des demandes cross-origin à l’application.
Notes
La spécification de AllowAnyOrigin
et AllowCredentials
est une configuration non sécurisée et peut entraîner une falsification de requête intersites. Le service CORS retourne une réponse CORS non valide lorsqu’une application est configurée avec les deux méthodes.
AllowAnyOrigin
affecte les demandes préliminaires et l’en-tête Access-Control-Allow-Origin
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.
SetIsOriginAllowedToAllowWildcardSubdomains: définit la propriété IsOriginAllowed de la stratégie comme une fonction qui permet aux origines de correspondre à un domaine générique configuré lors de l’évaluation de l’autorisation de l’origine.
options.AddPolicy("MyAllowSubdomainPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
Access-Control-Allow-Methods
. Pour plus d’informations, consultez la section Demandes de contrôle préalable.Pour autoriser l’envoi d’en-têtes spécifiques dans une requête CORS, appelées en-têtes de demande d’auteur, appelez WithHeaders et spécifiez les en-têtes autorisés :
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
AllowAnyHeader
affecte les demandes préliminaires et l’en-tête Access-Control-Request-Headers . Pour plus d’informations, consultez la section Demandes de contrôle préalable.
Une politique CORS Middleware correspondant à des en-têtes spécifiques spécifiés par WithHeaders
n'est possible que si les en-têtes envoyés dans Access-Control-Request-Headers
correspondent exactement aux en-têtes indiqués dans WithHeaders
.
Pour instance, envisagez une application configurée comme suit :
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware refuse une demande préliminaire avec l’en-tête de demande suivant, car Content-Language
(HeaderNames.ContentLanguage) n’est pas répertorié dans WithHeaders
:
Access-Control-Request-Headers: Cache-Control, Content-Language
L’application retourne une réponse 200 OK, mais ne renvoie pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin.
Par défaut, le navigateur n’expose pas tous les en-têtes de réponse à l’application. Pour plus d’informations, consultez Partage de ressources cross-origin du W3C (terminologie) : en-tête de réponse simple.
Les en-têtes de réponse disponibles par défaut sont les suivants :
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
La spécification CORS appelle ces en-têtes de réponse simples. Pour rendre d’autres en-têtes disponibles pour l’application, appelez WithExposedHeaders :
options.AddPolicy("MyExposeResponseHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.WithExposedHeaders("x-custom-header");
});
Les informations d’identification nécessitent une gestion spéciale dans une demande CORS. Par défaut le navigateur n’envoie pas d’informations d’identification avec une requête de cross-origin. Les informations d’identification incluent des cookies et des schémas d’authentification HTTP. Pour envoyer des informations d’identification avec une requête cross-origin, le client doit définir XMLHttpRequest.withCredentials
sur true
.
En utilisant XMLHttpRequest
directement :
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Utilisation de jQuery :
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Utilisation de l’API Fetch :
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
Le serveur doit autoriser les informations d’identification. Pour autoriser les informations d’identification cross-origin, appelez AllowCredentials :
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.AllowCredentials();
});
La réponse HTTP inclut un Access-Control-Allow-Credentials
en-tête, qui indique au navigateur que le serveur autorise les informations d’identification pour une requête cross-origin.
Si le navigateur envoie des informations d’identification mais que la réponse n’inclut pas d’en-tête valide Access-Control-Allow-Credentials
, le navigateur n’expose pas la réponse à l’application et la demande cross-origin échoue.
L’autorisation des informations d’identification cross-origin est un risque pour la sécurité. Un site web d’un autre domaine peut envoyer les informations d’identification d’un utilisateur connecté à l’application pour le compte de l’utilisateur à l’insu de l’utilisateur.
La spécification CORS indique également que la définition des origines sur "*"
(toutes les origines) n’est pas valide si l’en-tête Access-Control-Allow-Credentials
est présent.
Pour certaines requêtes CORS, le navigateur envoie une requête OPTIONS supplémentaire avant d’effectuer la requête réelle. Cette requête est appelée demande préliminaire. Le navigateur peut ignorer la demande préliminaire si toutesles conditions suivantes sont remplies :
Accept
, Accept-Language
, Content-Language
, Content-Type
ou Last-Event-ID
.Content-Type
, s'il est défini, a l'une des valeurs suivantes : application/x-www-form-urlencoded
multipart/form-data
text/plain
La règle sur les en-têtes de demande définis pour la demande cliente s’applique aux en-têtes définis par l’application en appelant setRequestHeader
sur l’objet XMLHttpRequest
. La spécification CORS appelle ces en-têtes des en-têtes de demande d’auteur. La règle ne s’applique pas aux en-têtes que le navigateur peut définir, tels que User-Agent
, Host
ou Content-Length
.
Voici un exemple de réponse similaire à la demande préliminaire effectuée à partir du bouton [Put test] dans la section Test CORS de ce document.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
La requête préliminaire utilise la méthode HTTP OPTIONS. Il peut inclure les en-têtes suivants :
User-Agent
.Si la demande préliminaire est refusée, l’application retourne une réponse 200 OK
, mais ne définit pas les en-têtes CORS. Par conséquent, le navigateur ne tente pas la requête cross-origin. Pour obtenir un exemple de demande préliminaire refusée, consultez la section Test CORS de ce document.
À l’aide des outils F12, l’application console affiche une erreur similaire à l’une des suivantes, en fonction du navigateur :
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5
. (Motif : la demande CORS n’a pas réussi). En savoir plusPour autoriser des en-têtes spécifiques, appelez WithHeaders :
options.AddPolicy("MyAllowHeadersPolicy",
policy =>
{
// requires using Microsoft.Net.Http.Headers;
policy.WithOrigins("http://example.com")
.WithHeaders(HeaderNames.ContentType, "x-custom-header");
});
Pour autoriser tous les en-têtes de demande d’auteur, appelez AllowAnyHeader :
options.AddPolicy("MyAllowAllHeadersPolicy",
policy =>
{
policy.WithOrigins("https://*.example.com")
.AllowAnyHeader();
});
Les navigateurs ne sont pas cohérents dans la façon dont ils définissent Access-Control-Request-Headers
. Si :
"*"
Accept
, Content-Type
et Origin
, ainsi que tous les en-têtes personnalisés que vous souhaitez prendre en charge.Lorsque la stratégie CORS est appliquée :
app.UseCors
dans Startup.Configure
.[EnableCors]
.ASP.NET Core répond à la demande d’OPTIONS en pré-vol.
L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors
ne prend actuellement pas en charge les demandes de contrôle préliminaire automatique.
La section Test CORS de ce document illustre ce comportement.
Lorsque CORS est activé avec la stratégie appropriée, ASP.NET Core répond généralement automatiquement aux demandes de contrôle préalable CORS. Dans certains scénarios, cela peut ne pas être le cas. Par exemple, l’utilisation de CORS avec le routage de point de terminaison.
Le code suivant utilise l’attribut [HttpOptions] pour créer des points de terminaison pour les demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
Pour obtenir des instructions sur le test du code précédent, consultez Test CORS avec routage de point de terminaison et [HttpOptions ].
L’en-tête Access-Control-Max-Age
spécifie la durée pendant laquelle la réponse à la demande de contrôle préliminaire peut être mise en cache. Pour définir cet en-tête, appelez SetPreflightMaxAge :
options.AddPolicy("MySetPreflightExpirationPolicy",
policy =>
{
policy.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
Cette section décrit ce qui se passe dans une requête CORS au niveau des messages HTTP.
<script>
pour recevoir la réponse. Les scripts sont autorisés à être chargés entre origines.La spécification CORS a introduit plusieurs nouveaux en-têtes HTTP qui activent les requêtes d’origine croisée. Si un navigateur prend en charge CORS, il définit automatiquement ces en-têtes pour les demandes d’origine croisée. Le code JavaScript personnalisé n’est pas nécessaire pour activer CORS.
Voici un exemple de demande d’origine croisée du bouton de test Valeurs vers https://cors1.azurewebsites.net/api/values
. En-tête Origin
:
En-têtes généraux
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
En-têtes de réponse
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
Dans les requêtes OPTIONS
, le serveur définit l’en-tête Access-Control-Allow-Origin: {allowed origin}
des en-têtes de réponse dans la réponse. Par exemple, l’exemple déployé, le Supprimer [EnableCors]boutonOPTIONS
contient les en-têtes suivants :
En-têtes généraux
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
En-têtes de réponse
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
En-têtes de requête
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
Dans les en-têtes de réponse précédents, le serveur définit l’en-tête Access-Control-Allow-Origin dans la réponse. La valeur https://cors1.azurewebsites.net
de cet en-tête correspond à l’en-tête Origin
de la demande.
Si AllowAnyOrigin est appelé,Access-Control-Allow-Origin: *
, la valeur de caractère générique est retournée. AllowAnyOrigin
autorise n’importe quelle origine.
Si la réponse n’inclut pas l’en-tête Access-Control-Allow-Origin
, la demande d’origine croisée échoue. Plus précisément, le navigateur interdit la demande. Même si le serveur retourne une réponse réussie, le navigateur ne rend pas la réponse disponible pour l’application cliente.
Par défaut, les navigateurs Chrome et Edge n’affichent pas les requêtes OPTIONS sous l’onglet réseau des outils F12. Pour afficher les demandes OPTIONS dans ces navigateurs :
chrome://flags/#out-of-blink-cors
ou edge://flags/#out-of-blink-cors
Firefox affiche les demandes OPTIONS par défaut.
Lors du déploiement sur IIS, CORS doit s’exécuter avant l’authentification Windows si le serveur n’est pas configuré pour autoriser l’accès anonyme. Pour prendre en charge ce scénario, le module IIS CORS doit être installé et configuré pour l’application.
L’exemple de téléchargement contient du code pour tester CORS. Consultez Guide pratique pour télécharger. L’exemple est un projet d’API avec Razor Pages ajoutées :
public class StartupTest2
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithMethods("PUT", "DELETE", "GET");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
Avertissement
WithOrigins("https://localhost:<port>");
doit uniquement être utilisé pour tester un exemple d’application similaire à l’exemple de code de téléchargement.
ValuesController
Voici les points de terminaison pour les tests :
[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
Testez l’exemple de code précédent à l’aide de l’une des approches suivantes :
dotnet run
à l’aide de l’URL par défaut de https://localhost:5001
.https://localhost:44398
.Utilisation d’un navigateur avec les outils F12 :
Sélectionnez le bouton Valeurs et passez en revue les en-têtes sous l’onglet Réseau.
Sélectionnez le bouton test PUT. Consultez Afficher les demandes OPTIONS pour obtenir des instructions sur l’affichage de la demande OPTIONS. Le test PUT crée deux requêtes, une demande de contrôle préliminaire OPTIONS et la demande PUT.
Sélectionnez le bouton GetValues2 [DisableCors]
pour déclencher une demande CORS ayant échoué. Comme indiqué dans le document, la réponse retourne 200 réussites, mais la demande CORS n’est pas effectuée. Sélectionnez l’onglet Console pour voir l’erreur CORS. Selon le navigateur, une erreur similaire à celle indiquée ci-après s'affiche :
L’accès à l’extraction à 'https://cors1.azurewebsites.net/api/values/GetValues2'
partir de l’origine 'https://cors3.azurewebsites.net'
a été bloqué par la stratégie CORS : aucun en-tête « Access-Control-Allow-Origin » n’est présent sur la ressource demandée. Si une réponse opaque répond à vos besoins, définissez le mode de la requête sur « no-cors » pour extraire la ressource avec CORS désactivé.
Les points de terminaison compatibles CORS peuvent être testés avec un outil, comme curl ou Fiddler. Lors de l’utilisation d’un outil, l’origine de la demande spécifiée par l’en-tête Origin
doit différer de celle de l’hôte qui reçoit la demande. Si la requête n’est pas d’origine croisée basée sur la valeur de l’en-tête Origin
:
La commande suivante utilise curl
pour émettre une demande OPTIONS avec des informations :
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
L’activation de CORS sur une base par point de terminaison à l’aide de RequireCors
ne prend actuellement pas en charge les demandes de contrôle préliminaire automatique. Considérez le code suivant qui utilise le routage de point de terminaison pour activer CORS:
public class StartupEndPointBugTest
{
readonly string MyPolicy = "_myPolicy";
// .WithHeaders(HeaderNames.ContentType, "x-custom-header")
// forces browsers to require a preflight request with GET
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyPolicy,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com",
"https://cors1.azurewebsites.net",
"https://cors3.azurewebsites.net",
"https://localhost:44398",
"https://localhost:5001")
.WithHeaders(HeaderNames.ContentType, "x-custom-header")
.WithMethods("PUT", "DELETE", "GET", "OPTIONS");
});
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors(MyPolicy);
endpoints.MapRazorPages();
});
}
}
Les éléments suivants TodoItems1Controller
fournissent des points de terminaison pour les tests :
[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($"ID = {id}");
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Testez le code précédent à partir de la page de test (https://cors1.azurewebsites.net/test?number=1
) de l’exemple déployé.
Les boutons Supprimer [EnableCors] et GET [EnableCors] réussissent, car les points de terminaison ont [EnableCors]
et répondent aux demandes préliminaires. Les autres points de terminaison échouent. Le bouton GET échoue, car javaScript envoie :
headers: {
"Content-Type": "x-custom-header"
},
Les éléments suivants TodoItems2Controller
fournissent des points de terminaison similaires, mais incluent du code explicite pour répondre aux demandes OPTIONS :
[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
// OPTIONS: api/TodoItems2
[HttpOptions]
public IActionResult PreflightRoute()
{
return NoContent();
}
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return BadRequest();
}
return ControllerContext.MyDisplayRouteInfo(id);
}
// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
[EnableCors] // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
ControllerContext.MyDisplayRouteInfo();
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
ControllerContext.MyDisplayRouteInfo();
[EnableCors] // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
ControllerContext.MyDisplayRouteInfo(id);
}
Le code précédent peut être testé en déployant l’exemple sur Azure. Dans la liste déroulante Contrôleur, sélectionnez Préliminaire, puis Définir le contrôleur. Tous les appels CORS aux points de terminaison TodoItems2Controller
réussissent.
Commentaires sur ASP.NET Core
ASP.NET Core est un projet open source. Sélectionnez un lien pour fournir des commentaires :
Événements
Championnats du monde Power BI DataViz
14 févr., 16 h - 31 mars, 16 h
Avec 4 chances d’entrer, vous pourriez gagner un package de conférence et le rendre à la Live Grand Finale à Las Vegas
En savoir plusEntrainement
Parcours d’apprentissage
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization