zoukankan      html  css  js  c++  java
  • JQuery.Ajax + 跨域 (crossDomain) + POST + JSON + WCF RESTful, 5大陷阱和解决方案

     JQuery.Ajax + 跨域 (crossDomain) + POST + JSON + WCF RESTful, 5大陷阱和解决方案

    最近在开发WSS RESTful服务的时候, 碰到了这些个纠结的问题. 在网上查找了半天, 找到n多种解决方案, 但是都是部分的, 要么是没有跨域的情况, 要么是没有post的情况, 要么不是用WCF进行开发. 可就是没有一个解决方案是将所有元素揉合在一起的, 真是奇怪, 然道我研究的是小众? 

    呵呵, 闲话少说, 直接上陷阱和解决方案:

    1. UriTemplate要和<endpointBehaviros><behavior><webHttp>配合使用

    用WCF开发REST就不用多说了. 唯一需要注意的是如果使用了UriTemplate来定义REST接口地址, 那么EndpointBehavior不能够继续使用<enableWebScript/>了, 而要改成使用<webHttp/>, 对应的, 在<service><endpoint>中behaviorConfiguration也要设置成WebBehavior.

    复制代码
          <endpointBehaviors>
            <!--<behavior name="AjaxBehavior">
              <enableWebScript/>
            </behavior>-->
            <behavior name="WebBehavior">
              <webHttp />
            </behavior>
          </endpointBehaviors>
    复制代码

    2. 为WebOperation指定输入输出的格式

    由于使用了webHttp behavior, 我们需要在ServiceContract中显示指定WebOperation的输入输出msg格式. 否则会在运行时报错找不到对应的序列化反序列化器.

    [OperationContract(Name="CreateNew")]
    [WebInvoke(RequestFormat=WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json, UriTemplate="/users", Method="POST")]
    string CreateNew(WSSAccount account); 

    3. 为了满足JQuery.Ajax, 需要改造WCF RESTful service

    这一步困扰了我好久, JQuery.Ajax在POST的时候是没法设置ContentType的. 网上好多人讨论这个问题, 我想应该是一个bug或者至少是一个设计的限制. 整体表现就是设置了没有任何效果. 在这种情况下去调用WCF写的RESTful Service就坑爹了. 因为JQuery.Ajax在POST的时候永远将contentType设置成application/x-www-unencoded; 而WCF默认的ContentTypeMapper永远只在收到ContentType=application/json的时候才会用JSON来解析Body中的数据...

    没办法了, 只有改写ContentTypeMapper:

    复制代码
        public class RESTContentTypeMapper : WebContentTypeMapper
        {
            public override WebContentFormat GetMessageFormatForContentType(string contentType)
            {
                if (contentType.IndexOf("json", StringComparison.CurrentCultureIgnoreCase) != -1)
                {return WebContentFormat.Json;
                }
                else if (contentType.IndexOf("xml", StringComparison.CurrentCultureIgnoreCase) != -1)
                {return WebContentFormat.Xml;
                }
                else
                {return WebContentFormat.Json;
                }
            }
        }
    复制代码

    强行将WebContentFormat.Json作为默认格式, 然后在<system.serviceModel><standardEndpoints>中配置如下:

        <standardEndpoints>
          <webHttpEndpoint>
            <standardEndpoint name="RESTEndpoint" contentTypeMapper="Webus.WSS.Core.RESTful.RESTContentTypeMapper, Core" />
          </webHttpEndpoint>
        </standardEndpoints>

    注意, 这个standardEndpoints只在.net4.0/4.5中才有效, 如果你用的老版本, 那还是算了吧. 配置好了之后, 在<service><endpoint>中endpointConfiguration要设置成"RESTEndpoint", kind要设置成"webHttpEndpoint". 

    下面来一个完整的配置例子:

     View Code

    <system.serviceModel>
    <services>
    <service name="s_Authentication" behaviorConfiguration="DefaultBehavior">
    <host>
    <baseAddresses>
    <add baseAddress="http://wss.gdtsearch.com/wss/services/test.Authentication" />
    </baseAddresses>
    </host>
    <endpoint name="Authentication_Web"
    address="Authentication_Web"
    binding="webHttpBinding"
    bindingConfiguration="webHttpBindingJSONP"
    contract="Webus.WSS.Core.Authentication.IWSSAuthentication"
    behaviorConfiguration="WebBehavior"
    endpointConfiguration="RESTEndpoint"
    kind="webHttpEndpoint"
    bindingNamespace="http://gdtsearch.com/wss/services/Authentication" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    </service>
    </services>
    <standardEndpoints>
    <webHttpEndpoint>
    <standardEndpoint name="RESTEndpoint" contentTypeMapper="Webus.WSS.Core.RESTful.RESTContentTypeMapper, Core" />
    </webHttpEndpoint>
    </standardEndpoints>

    <behaviors>
    <endpointBehaviors>
    <!--<behavior name="AjaxBehavior">
    <enableWebScript/>
    </behavior>-->
    <behavior name="WebBehavior">
    <webHttp />
    </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
    <behavior name="DefaultBehavior">
    <serviceMetadata httpGetEnabled="True"/>
    <serviceDebug includeExceptionDetailInFaults="True"/>
    </behavior>
    </serviceBehaviors>
    </behaviors>

    <bindings>
    <webHttpBinding>
    <binding name="webHttpBindingJSONP" crossDomainScriptAccessEnabled="true"/>
    </webHttpBinding>
    </bindings>
    </system.serviceModel>

    4. JQuery.Ajax - crossDomain要设置为true / post的数据要首先转换成JSON格式

    我用于POST数据的代码如下, 却总是得到一个HTTP400的错误, 打开Fiddler一看, 发现Body中的数据根本不是JSON格式, 而是普通的类似于querystring中的name-Value组合的字符串.

    复制代码
        function createNewAccount(email, password) {var account = {
                    "Email": email,
                    "Password": CryptoJS.MD5(password).toString(CryptoJS.enc.Base64),
                    "Enabled": true
                };
    
            $.ajax({
                type: "POST",
                crossDomain: true,
                url: "http://wss.gdtsearch.com/wss/services/test.Authentication/Authentication_Web/users",
                data: account,
                dataType: "json",
                success: function(msg) {
                    alert(msg);
                },
                error: function(a, b, c) {
                    alert(a + b + c);
                }
            });
        }
    复制代码

    没办法, 只好手动将数据转换成JSON了, 好在我找到了一个不错的plugin: https://code.google.com/p/jquery-json/. 代码简单改改就能够用了:

    复制代码
            $.ajax({
                type: "POST",
                crossDomain: true,
                url: "http://wss.gdtsearch.com/wss/services/test.Authentication/Authentication_Web/users",
                data: $.toJSON(account),
                dataType: "json",
                success: function(msg) {
                    alert(msg);
                },
                error: function(a, b, c) {
                    alert(a + b + c);
                }
            });
    复制代码

    5. JQuery.ajax的跨域只支持XMLHTTPRequest

    一切就绪之后, 终于可以开始测试了. 打开Chrome, 打开Fiddler, 访问页面, 点击按钮运行,,, ,,, ,,, 浏览器貌似正常, Fiddler得到一个HTTP200~! 唯一的遗憾是Chrome的Console会出现一个跨域的JS错误. 看来通过设置crossDomain=true确实可以进行跨域访问, 但是并不完美.

    打开IE, 再次测试,,, 报错!? Fiddler中没有任何反应, 甚至连Request都没有发出去, 奇怪? googling... 答案在StackOverflow的一篇文章中找到: http://stackoverflow.com/questions/3362474/jquery-ajax-fails-in-ie-on-cross-domain-calls

    OTOH, this page mentions that IE7 and eariler cannot do cross domain calls, but IE8 can, using a different object than XMLHttpRequest, the one JQuery uses. Could you check if XDomainRequest works?

    也就是说IE8用的所谓XDomainRequest而非XMLHttpRequest, 但是JQuery只支持XMLHttpRequest... 所以想用IE的同学就断了这个念想吧...

    小结 - 

    研究了两天, 总算将JQuery.Ajax + crossDomain + POST + JSON + WCF RESTful 串起来跑通了, 可是也只能够在Chrome上面跑, FF和Safari没有试, 但是我估计能够行. 硬伤是IE不行, 没想到是这么个结果, 太遗憾了. 不过要实现功能, 也并非无路可走, 关键还是要看你在ServiceContract中要不要用UriTemplate, 如果你能够放弃自己设计RESTful接口的主张, 全盘接受微软的一套, 那么简单用<enableWebScript>+JSONP就行啦; 如果你坚持自己设计RESTful接口, 不妨考虑用NodeJS搭建一个本地的proxy svc来解决跨域的问题. 反正仁者见仁, 智者见智, 动手能力强的TX们自己取舍吧.

     
     
    分类: 其它
  • 相关阅读:
    数仓备机DN重建:快速修复你的数仓DN单点故障
    深度学习分类任务常用评估指标
    云小课 | MRS基础入门之HDFS组件介绍
    华为云数据库GaussDB(for Cassandra)揭秘第二期:内存异常增长的排查经历
    为什么vacuum后表还是继续膨胀?
    Go 自定义日志库
    Go time包
    Go 文件操作
    Go 包相关
    【程序人生】跟小伙伴们聊聊我有趣的大学生活和我那两个好基友!
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3286144.html
Copyright © 2011-2022 走看看