web-dev-qa-db-fra.com

Comment prendre en charge le verbe HTTP OPTIONS dans l'application ASP.NET MVC / WebAPI

J'ai configuré une application Web ASP.NET à partir d'un modèle d'API MVC 4/Web. Il semble que les choses fonctionnent vraiment bien - pas de problèmes que je sache. J'ai utilisé Chrome et Firefox pour parcourir le site. J'ai testé avec Fiddler et toutes les réponses semblent être sur l'argent.

Alors maintenant, je vais écrire un simple Test.aspx pour utiliser cette nouvelle API Web. Les parties pertinentes du script:

<script type="text/javascript">
    $(function () {

        $.ajax({
            url: "http://mywebapidomain.com/api/user",
            type: "GET",
            contentType: "json",
            success: function (data) {

                $.each(data, function (index, item) {

                    ....

                    });
                }
                );

            },
            failure: function (result) {
                alert(result.d);
            },

            error: function (XMLHttpRequest, textStatus, errorThrown) {
                alert("An error occurred, please try again. " + textStatus);
            }

        });

    });
</script>

Cela génère un en-tête de requête:

OPTIONS http://Host.mywebapidomain.com/api/user HTTP/1.1
Host: Host.mywebapidomain.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://mywebapidomain.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type
Connection: keep-alive

En l'état, l'API Web renvoie une méthode 405 non autorisée.

HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 13:28:12 GMT
Content-Length: 96

<Error><Message>The requested resource does not support http method 'OPTIONS'.</Message></Error>

Je comprends que le verbe OPTIONS n’est pas câblé dans les contrôleurs API Web par défaut ... Donc, j’ai placé le code suivant dans UserController.cs:

// OPTIONS http-verb handler
public HttpResponseMessage OptionsUser()
{
    var response = new HttpResponseMessage();
    response.StatusCode = HttpStatusCode.OK;
    return response;
}

... et cela a éliminé l'erreur 405 Method Not Allowed, mais la réponse est complètement vide - aucune donnée n'est renvoyée:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 12:56:21 GMT
Content-Length: 0

Il doit y avoir une logique supplémentaire ... Je ne sais pas comment coder correctement la méthode Options ou si le contrôleur est même le bon endroit pour mettre le code. Bizarre (à mon avis) que le site de l'API Web réponde correctement lorsqu'il est visualisé depuis Firefox ou Chrome, mais l'appel .ajax mentionné ci-dessus est erroné. Comment gérer le contrôle "preflight" dans le code .ajax? Peut-être devrais-je aborder cette question dans la logique .ajax du client? Ou, s'il s'agit d'un problème côté serveur en raison de la non gestion du verbe OPTIONS.

Quelqu'un peut-il aider? Ce doit être un problème très commun et je m'excuse si on a répondu ici. J'ai cherché mais n'ai trouvé aucune réponse qui ait aidé.

UPDATE IMHO, il s’agit d’un problème côté client qui a à voir avec le code Ajax JQuery ci-dessus. Je dis cela parce que Fiddler ne montre aucun en-tête d'erreur 405 lorsque j'accède à mywebapidomain/api/user à partir d'un navigateur Web. Le seul endroit où je peux reproduire ce problème provient de l'appel JQuery .ajax (). En outre, l'appel Ajax identique ci-dessus fonctionne correctement lorsqu'il est exécuté sur le serveur (même domaine).

J'ai trouvé un autre message: demande du prototype AJAX envoyée sous forme d'OPTIONS plutôt que de GET; résultat: erreur 501 qui semble être lié, mais j'ai bricolé leurs suggestions sans succès . Apparemment, JQuery est codé de sorte que, si une requête Ajax est interdomaine (quelle mine est la mienne), elle ajoute quelques en-têtes qui déclenchent l'en-tête OPTIONS d'une manière ou d'une autre.

'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,

Il semble simplement qu'il devrait y avoir une meilleure solution disponible que de modifier le code principal dans JQuery ...

La réponse ci-dessous suppose qu'il s'agit d'un problème côté serveur. Peut-être, je suppose, mais je me penche vers le client et appeler un fournisseur d'hébergement ne va pas aider.

77
rwkiii

Comme Daniel A. White l'a dit dans son commentaire, la requête OPTIONS est probablement créée par le client dans le cadre d'une requête JavaScript interdomaine. Cette opération est effectuée automatiquement par les navigateurs compatibles CORS (Cross Origin Resource Sharing). La demande est une demande préliminaire ou préalable à la demande réelle AJAX afin de déterminer les verbes et les en-têtes de demande pris en charge pour CORS. . Le serveur peut choisir de le prendre en charge pour aucun, tout ou partie des verbes HTTP.

Pour compléter le tableau, la demande AJAX comporte un en-tête "Origine" supplémentaire, qui identifie l'emplacement d'origine de la page d'origine hébergeant le code JavaScript. Le serveur peut choisir de prendre en charge les demandes émanant de n’importe quelle origine, ou simplement d’un ensemble d’origines de confiance connues. Autoriser toute origine est un risque pour la sécurité, car cela peut augmenter le risque de falsification de requête intersite (CSRF).

Donc, vous devez activer CORS.

Voici un lien qui explique comment procéder dans l’API Web ASP.Net.

http://www.asp.net/web-api/overview/security/enabling-cross-Origin-requests-in-web-api#enable-cors

L’implémentation décrite ici vous permet de spécifier, entre autres

  • Prise en charge de CORS par action, par contrôleur ou au niveau mondial
  • Les origines soutenues
  • Lorsque vous activez CORS à un niveau de contrôleur ou global, les verbes HTTP pris en charge
  • Si le serveur prend en charge l'envoi d'informations d'identification avec des demandes d'origine croisée

En général, cela fonctionne bien, mais vous devez être conscient des risques de sécurité, en particulier si vous autorisez les requêtes croisées sur l'origine à partir de n'importe quel domaine. Réfléchissez bien avant de permettre cela.

Pour ce qui est des navigateurs qui supportent CORS, Wikipedia indique que les moteurs suivants le supportent:

  • Gecko 1.9.1 (FireFox 3.5)
  • WebKit (Safari 4, Chrome 3)
  • MSHTML/Trident 6 (IE10) avec prise en charge partielle dans IE8 et 9
  • Presto (Opéra 12)

http://en.wikipedia.org/wiki/Cross-Origin_resource_sharing#Browser_support

48
Mike Goodwin

La réponse de Mike Goodwin est excellente, mais lorsque je l’ai essayée, il semblait que cela visait MVC5/WebApi 2.1. Les dépendances pour Microsoft.AspNet.WebApi.Cors ne fonctionnaient pas bien avec mon projet MVC4.

Le moyen le plus simple d'activer CORS sur WebApi avec MVC4 était le suivant.

Notez que j'ai tout autorisé, je vous suggère de limiter les paramètres Origin uniquement aux clients que vous souhaitez que votre API serve. Tout autoriser est un risque pour la sécurité.

Web.config:

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
      </customHeaders>
    </httpProtocol>
</system.webServer>

BaseApiController.cs:

Nous faisons cela pour autoriser le verbe OPTIONS http

 public class BaseApiController : ApiController
  {
    public HttpResponseMessage Options()
    {
      return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
    }
  }
89
Oliver

Ajoutez simplement ceci à votre méthode _Application_OnBeginRequest_ (cela activera la prise en charge globale de CORS pour votre application) et "gérez" les demandes de contrôle en amont:

_var res = HttpContext.Current.Response;
var req = HttpContext.Current.Request;
res.AppendHeader("Access-Control-Allow-Origin", req.Headers["Origin"]);
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");

// ==== Respond to the OPTIONS verb =====
if (req.HttpMethod == "OPTIONS")
{
    res.StatusCode = 200;
    res.End();
}
_

* sécurité: sachez que cela activera les requêtes ajax de n'importe où sur votre serveur (vous pouvez uniquement autoriser une liste d'origines/urls séparées par des virgules si vous préférez).

J'ai utilisé le client actuel Origin au lieu de _*_ car cela autorisera les informations d'identification => la définition de _Access-Control-Allow-Credentials_ sur true active la gestion des sessions entre navigateurs.

vous devez également activer les verbes delete and put, patch et options dans votre webconfig section _system.webServer_, sinon IIS les bloquera:

_<handlers>
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
_

j'espère que cela t'aides

22
Chtiwi Malek

Après avoir rencontré le même problème dans un projet Web API 2 (et être incapable d'utiliser les packages CORS standard pour des raisons qui ne valaient pas la peine d'être expliquées ici), j'ai pu résoudre ce problème en implémentant un DelagatingHandler personnalisé:

public class AllowOptionsHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        if (request.Method == HttpMethod.Options &&
            response.StatusCode == HttpStatusCode.MethodNotAllowed)
        {
            response = new HttpResponseMessage(HttpStatusCode.OK);
        }

        return response;
    }
}

Pour la configuration de l'API Web:

config.MessageHandlers.Add(new AllowOptionsHandler());

Notez que j'ai également les en-têtes CORS activés dans Web.config, similaires à certaines des réponses publiées ici:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
    <remove name="WebDAVModule" />
  </modules>

  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Headers" value="accept, cache-control, content-type, authorization" />
      <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
    </customHeaders>
  </httpProtocol>

  <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="TRACEVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>

Notez que mon projet n'inclut pas MVC, uniquement Web API 2.

15
defines

J'ai réussi à surmonter les erreurs 405 et 404 générées lors de demandes d'options ajax préalables au vol uniquement par du code personnalisé dans global.asax.

protected void Application_BeginRequest()
    {            
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
        {
            //These headers are handling the "pre-flight" OPTIONS call sent by the browser
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
            HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
            HttpContext.Current.Response.End();
        }
    }

PS: Prenez en compte les problèmes de sécurité lorsque vous autorisez tout *.

J'ai dû désactiver CORS car il renvoyait l'en-tête 'Access-Control-Allow-Origin' contenant plusieurs valeurs.

Aussi nécessaire ceci dans web.config:

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
  <remove name="OPTIONSVerbHandler"/>
  <remove name="TRACEVerbHandler"/>
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>

Et app.pool doit être défini sur le mode intégré.

8
lukalev

J'ai eu le même problème. Pour moi, le correctif consistait à supprimer le type de contenu personnalisé de l'appel jQuery AJAX. Les types de contenu personnalisés déclenchent la demande de pré-vol. J'ai trouvé ça:

Le navigateur peut ignorer la demande de contrôle en amont si les conditions suivantes sont remplies:

La méthode de demande est GET, HEAD ou POST, et

L'application ne définit aucun en-tête de requête autre que Accept, Accept-Language, Content-Language, Content-Type ou Last-Event-ID, . et

L'en-tête Content-Type (s'il est défini) est l'un des suivants:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Depuis cette page: http://www.asp.net/web-api/overview/security/enabling-cross-Origin-requests-in-web-api (sous "Demandes de contrôle en amont")

7
Dominik

Dans l’API Web ASP.NET 2, la prise en charge de CORS a été ajoutée. Veuillez vérifier le lien [ http://www.asp.net/web-api/overview/security/enabling-cross-Origin-requests-in-web-api ]

3
refactor
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" )
        {
            Response.Clear();
            Response.StatusCode = 200;
            Response.End();
        }
    }
2
yongfa365

J'ai eu le même problème, et voici comment je l'ai corrigé:

Il suffit de jeter ceci dans votre web.config:

<system.webServer>
    <modules>
      <remove name="WebDAVModule" />
    </modules>

    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" />
        <add name="Access-Control-Allow-Headers" value="accept, authorization, Content-Type" />
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>

    <handlers>
      <remove name="WebDAV" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>
1
Erti-Chris Eelmaa

Moi aussi j'ai fait face au même problème.

Suivez les étapes ci-dessous pour résoudre le problème de conformité (CORS) dans les navigateurs.

Incluez REDRock dans votre solution avec la référence Cors. Inclure une référence WebActivatorEx à la solution API Web.

Ajoutez ensuite le fichier CorsConfig dans le dossier App_Start de l’API Web.

[Assembly: PreApplicationStartMethod(typeof(WebApiNamespace.CorsConfig), "PreStart")]

namespace WebApiNamespace
{
    public static class CorsConfig
    {
        public static void PreStart()
        {
            GlobalConfiguration.Configuration.MessageHandlers.Add(new RedRocket.WebApi.Cors.CorsHandler());
        }
    }
}

Grâce à ces modifications, j'ai pu accéder à la Webapi dans tous les navigateurs.

1