web-dev-qa-db-fra.com

Problème d'envoi des données JSON de JQuery à WCF REST méthode

Je ne parviens pas à faire en sorte que jquery poste certaines données json sur une méthode de repos que j'ai sur mon service WCF.

Du côté de la WCF, voici le contrat d’exploitation:

[OperationContract]
[WebInvoke(Method = "POST",
           BodyStyle = WebMessageBodyStyle.Bare,
           RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "PostSomething")]
MyResult PostSomething(MyRequest request);

MyResult et MyRequest sont marqués avec tous les attributs DataContract et DataMember nécessaires et le service expose le point de terminaison WebHttp.

Du côté de JQuery, voici mon appel de fonction:

var jsonStr = JSON.stringify(reqObj);

$.ajax({
    type: "POST",
    dataType: "json",
    url: "http://localhost/MyService/PostSomething",
    contentType: "application/json; charset=utf-8",
    data: jsonStr,
    success: function (html) {
        alert(html);
    }
});

cette demande n'atteint jamais ma méthode (je reçois une méthode 405 non autorisée à chaque fois), et en regardant dans Charles, la demande ressemble à ceci:

OPTIONS /MyService/PostSomething HTTP/1.1
Host: localhost
Cache-Control: max-age=0
Access-Control-Request-Method: POST
Origin: null
Access-Control-Request-Headers: Content-Type, Accept
Accept: */*
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

deux choses qui sont étranges à ce sujet:

  1. la méthode est OPTIONS pas POST
  2. le type de contenu (dans un autre onglet) affiche text/html; charset=UTF-8 au lieu de json
  3. les données JSON ne sont pas visibles

Cependant, si je modifie la requête dans Charles afin que ses en-têtes soient similaires à la solution ici , alors tout fonctionne:

POST /MyService/PostSomething HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: localhost
Content-Length: 152

{"Id":"", "Name":"testspot","Description":"test" } 

en regardant des tutoriels et d'autres questions ici, d'autres ont réussi à faire en sorte que JQuery poste sur une méthode WCF REST comme celle-ci, et je ne comprends pas ce que je fais de mal ici.

oh, pour mettre un peu de contexte, c'est un service WCF 4 et j'utilise JQuery 1.4.4.

Merci,

UPDATE:

Après un peu plus de lecture et merci à Darrel de m'avoir dirigé vers la spécification interdomaine, j'ai réussi à aller un peu plus loin en apportant de petites modifications à mon service, sur l'interface de service:

[OperationContract]
[WebInvoke(Method = "*",
           BodyStyle = WebMessageBodyStyle.Bare,
           RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "PostSomething")]
MyResult PostSomething(MyRequest request);

et dans la mise en œuvre, je dois vérifier si les demandes entrantes concernent OPTIONS et, dans ce cas, renvoyer des en-têtes plutôt que d'effectuer le travail prévu:

if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST");
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept");

    return null;
}

la méthode est ensuite appelée deux fois, la première fois que le serveur renvoie la valeur null mais ajoute des en-têtes au client, puis la demande est effectuée avec la méthode POST et le serveur procède ensuite et traite normalement la demande. .

22
theburningmonk

Cela semble être une solution de Firefox pour éviter les appels interdomaines. Voir http://www.petefreitag.com/item/703.cfm

La spécification pour ceci est ici http://www.w3.org/TR/cors/ et après une lecture très brève, il semble que, du fait que vous effectuez un appel entre domaines, votre service est censé implémenter le OPTIONS et renvoyer des en-têtes permettant d'envoyer la méthode POST.

7
Darrel Miller

La mise à jour de la question contenant une solution proposée présente certains problèmes. Le problème est que si votre entrée ne prend pas en charge la méthode POST, la demande OPTIONS ne renvoie pas les en-têtes autorisés corrects. Il ne s’agit pas vraiment de savoir quelles méthodes sont réellement autorisées sur le point de terminaison WCF; c’est simplement dire artificiellement que "POST" est autorisé pour tous les points de terminaison de l’application lorsqu'un client effectue une demande OPTIONS (qui est en réalité le client qui demande ce qui est pris en charge. ). 

Ceci est probablement OK, si vous ne vous appuyez pas vraiment sur les informations de la méthode OPTIONS pour vous renvoyer une liste valide de méthodes (comme c'est le cas avec certaines demandes CORS) - mais si vous le faites, vous devrez faire quelque chose comme: la solution à cette question: Comment gérer une demande Ajax JQUERY POST avec WCF self-Host

Fondamentalement, chaque terminal doit implémenter:

Webinvoke(Method="OPTIONS", UriTemplate="")

et appelez une méthode appropriée qui charge les en-têtes appropriés dans la réponse (y compris la liste "Accès-contrôle-autorisation-appropriée" appropriée pour ce noeud final) à l'appelant. Il est un peu dommage que les points de terminaison WCF hébergés ne le fassent pas automatiquement pour nous, mais il s'agit d'une solution de contournement qui permet un contrôle plus fin du point de terminaison. Dans cette solution, les en-têtes de réponse appropriés sont chargés lors de la mise en œuvre du noeud final:

public void GetOptions()
    {
        // The data loaded in these headers should match whatever it is you support on the endpoint
        // for your application. 
        // For Origin: The "*" should really be a list of valid cross site domains for better security
        // For Methods: The list should be the list of support methods for the endpoint
        // For Allowed Headers: The list should be the supported header for your application

        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization");
    }

En plus de cela, vous devez définir l'indicateur "CrossDomainScriptAccessEnabled" dans le fichier web.config pour le point de terminaison de la liaison ou dans le code pour le WebHttpBinding lors de la configuration du point de terminaison. Sinon, vous vous retrouvez encore dans votre réponse en-tête lorsque vous dites "Access-Control-Allow-Origin" est "*" (ou une liste d'URL)

3
jeremyh

Mettre à jour:

Essayez de mettre .svc après MyService pour que l’URL soit lue

http://localhost/MyService.svc/PostSomething

J'y travaillais moi-même l'autre jour et je suis tombé sur un article sur le blog de Rick Strahl:

http://www.west-wind.com/weblog/posts/324917.aspx

Cela fonctionne parfaitement pour moi, alors essayez-le!

J'espère que cela pourra aider! :)

1
Yngve B-Nilsen

Avez-vous utilisé Webhttpbinding dans votre web.config?

seul webhttpbinding supporte json.

0
Shiraz Bhaiji

Je vais juste poster une réponse courte qui m'a aidé, parce que d'autres réponses ne l'ont pas.

  • Scénario: appel ajax au service wcf.
  • Cause de l'erreur: demande automatique d'OPTIONS de ajax avant d'envoyer la demande POST . La première demande n'a pas pu être traitée par mon service.
  • Solution: autoriser la demande OPTIONS et y répondre.

Qu'as tu besoin de faire:

  1. Ajoutez ceci à web.config:

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

  2. Ajoutez ceci à Global.asax.cs (si vous n'avez pas ce fichier dans votre solution, créez-le ensuite avec: Ajouter nouvel élément => Visual C # => Classe d'application globale (le nom par défaut est "Global.asax")):

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
                        HttpContext.Current.Response.End();
    }
    
0
alexkovelsky