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".

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

<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们自己取舍吧.

 
 
分类: 其它

JQuery.Ajax + 跨域 (crossDomain) + POST + JSON + WCF RESTful, 5大陷阱和解决方案的更多相关文章

  1. [经验] - JQuery.Ajax + 跨域 (crossDomain) + POST + JSON + WCF RESTful, 5大陷阱和解决方案

    最近在开发WSS RESTful服务的时候, 碰到了这些个纠结的问题. 在网上查找了半天, 找到n多种解决方案, 但是都是部分的, 要么是没有跨域的情况, 要么是没有post的情况, 要么不是用WCF ...

  2. jQuery $.ajax跨域-JSONP获取JSON数据(转载)

    Asynchronous JavaScript and XML (Ajax ) 是驱动新一代 Web 站点(流行术语为 Web 2.0 站点)的关键技术.Ajax 允许在不干扰 Web 应用程序的显示 ...

  3. NodeJ node.js Jquery Ajax 跨域请求

    Jquery + Ajax 跨域请求 说白了就是前台请求ajax数据(JSON)但是请求的数据不在本地的绝对路径下,接口数据 是没有这个安全性的我对外公开的接口数据,只要你找到接口你就可以使用里面的数 ...

  4. 关于JQuery Ajax 跨域 访问.net WebService

    关于这个 jQuery Ajax跨域访问 WebService 前天整了好几个小时没整明白 今天再看一下 结果突然就顿悟了 1.建一个空webApplication --添加--新建项--web服务( ...

  5. jquery+ajax跨域请求webservice

    最近几天在学习webservice...在学习的时候便想到用ajax的方式去请求webservice.. 一直在测试..如果这个被请求的webservice和自己使用的是同一个端口号.则不用考虑那aj ...

  6. jQuery ajax跨域请求的解决方法

    在Ajax应用中,jQuery的Ajax请求是非常容易而且方便的,但是初学者经常会犯一个错误,那就是Ajax请求的url不是本地或者同一个服务器下面的URI,最后导致虽然请求200,但是不会返回任何数 ...

  7. jquery ajax跨域请求详解

    本文章来给大家详细jquery中的ajax跨域请求, 在JQuery对于Ajax的跨域请求有两类解决方案,不过都是只支持get方式.分别是JQuery的jquery.ajax jsonp格式和jque ...

  8. jquery ajax跨域的完美解决方法(jsonp方式)

    ajax跨域请求的问题,JQuery对于Ajax的跨域请求有两类解决方案,不过都是只支持get方式,接下来为大家详细介绍下客户端JQuery.ajax的调用代码     今天在项目中需要做远程数据加载 ...

  9. AJAX跨域POST发送json时,会先发送一个OPTIONS预请求

    我们会发现,在很多post,put,delete等请求之前,会有一次options请求. 根本原因就是,W3C规范这样要求了!在跨域请求中,分为简单请求(get和部分post,post时content ...

随机推荐

  1. MAC使用小技巧(二)

    一.Safari-->广告数量不足 --原因:DNS被拦截,被恶意推送广告. ----------------------------- [ 思路 ] 修改hosts文件 $ cd /etc $ ...

  2. 严重:IOException while loading persisted sessions:java.io.EOFException.

    1.错误叙述性说明 严重:IOException while loading persisted sessions:java.io.EOFException. java.io.EOFException ...

  3. 通过扩展改善ASP.NET MVC的验证机制[实现篇]

    原文:通过扩展改善ASP.NET MVC的验证机制[实现篇] 在<使用篇>中我们谈到扩展的验证编程方式,并且演示了本解决方案的三大特性:消息提供机制的分离.多语言的支持和多验证规则的支持, ...

  4. POJ 3067 Japan 树状数组求逆序对

    题目大意:有两排城市,这两排城市之间有一些路相互连接着,求有多少条路相互交叉. 思路:把全部的路先依照x值从小到大排序,x值同样的依照y值从小到大排序,然后插入边的时候,先找有多少比自己y值小的,这些 ...

  5. (12) MVC5 EF6 Bootstrap3

    MVC5 + EF6 + Bootstrap3 (12) 新建数据 系列教程:MVC5 + EF6 + Bootstrap3 上一节:MVC5 + EF6 + Bootstrap3 (11) 排序.搜 ...

  6. x240 uefi ubuntu 12.04.4

    http://askubuntu.com/questions/355034/ubuntu-12-04-3-lts-installation-failed-says-grub-installation- ...

  7. QT中出现“无法解析的外部符号”错误

    串口中我要把奇偶校验等enum数据对应写到一个ComboBox中去,所以需要在我的Settings类中加入一个函数用来转换. 这里,我首先在settings.h的class Settings中加上对该 ...

  8. 微软的权限框架Asp.Net Identity

    Asp.Net Identity 深度解析 之 注册登录的扩展   关于权限每个系统都有自己的解决方案,今天我们来讨论一下微软的权限框架Asp.Net Identity ,介绍如下  http://w ...

  9. ubuntu12.04 内核编译 记录

    近期学习linux这门课,做实验要编译系统内核,然后..五一没事就捣鼓了一上午,还好成功了,以下就写下过程吧. 注意:以下过程的有些make 这类的命令 可能要获取权限 1.開始时能够查一下自己如今系 ...

  10. 启用密码管理之前创建的用户连接Oracle报ORA-28002处理一则

    处理方法其实很简单.只要:     alter user <username> identified by <same password>;  这个操作后,恢复正常了 下面作个 ...