前言javascript
微软宣布.NET开源,因而我等夹着尾巴混迹OSC的C#程序员终于能够昂首挺胸作人了!java
不知道为什么,不少人痛恨.微软的产品,其中不乏没怎么用过C#就开始黑的,只有用过才知道,微软的框架搞出问题来,找解决方案是个极为痛苦的过程,最近几天搞WCF RESTful 服务,以及使用Jquery 跨域调用,出来一系列问题,为了找解决方案,英语阅读能力直线上升,已经到了不看英文文档不舒服的程度!程序员
因为踩坑太多,感受又像玩扫雷,又像打地鼠,为了防止之后继续踩坑,赶忙把遇到的一些印象深入的问题摘录下来。web
目前这项目服务端使用 EF 6.0 WCF 4.0 ,服务端作了个动态建立服务,动态替换服务实现类DLL的机制,而后服务发布起来后出现了一系列问题:ajax
1 实体类做参数和返回值时,序列化和反序列化出错json
假若有这样一个实体类
c#
/// <summary> /// 员工表 /// </summary> [DataContract] public class Employee { /// <summary> /// 员工ID /// </summary> [DataMember] [Key, Required] public int EmployeeId { get; set; } /// <summary> /// 员工名 /// </summary> [DataMember] [StringLength(32)] public string EmployeeName { get; set; } /// <summary> /// 员工号 /// </summary> [DataMember] [StringLength(32)] public string EmployeeCartId { get; set; } /// <summary> /// 建立时间 /// </summary> [DataMember] public DateTime CreatTime { get; set; } /// <summary> /// 建立者ID /// </summary> [DataMember] public int CreaterID { get; set; } /// <summary> /// 密码 /// </summary> [DataMember] [StringLength(32)] public string Password { get; set; } /// <summary> /// 是否使用 /// </summary> [DataMember] public bool Enable { get; set; } }
以及服务接口的部分方法
跨域
[OperationContract] [WebInvoke(UriTemplate = "sys_employee", Method = "POST", ResponseFormat = WebMessageFormat.Json)] Employee AddEmployee(Employee employee); [OperationContract] [WebGet(UriTemplate = "sys_employee/{id}",ResponseFormat = WebMessageFormat.Json)] Employee GetEmployeeById(string id);
还有测试的JS代码浏览器
<script type="text/javascript"> $(function () { var jp = { "EmployeeId": 5, "EmployeeName": "335", "EmployeeCartId": "335", "CreatTime": "/Date(1242357713797+0800)/", "CreaterID": 1, "Password": "1", "Enable": true }; $.ajax({ data: JSON.stringify(jp) , type: "POST", url: "http://127.0.0.1:8766/sys_employee", // contentType: "application/json; charset=utf-8", // dataType: "jsonp", dataType: "json", success: function (msg) { alert(msg.redata); }, error: function (xhr, error) { alert(error); } }); }); </script>
这里要说明踩坑时候遇到几个小问题:安全
WCF发布 RESTful 服务须要添加System.ServiceModel.Web的引用
JSON的日期格式是 "/Date(1242357713797+0800)/" ,若是写了“2014-11-14”这样序列化会出错
使用Jquery的ajax 跨域调用的时候,设置data="JSONP" Method自动转为GET;设置了contentType: "application/json; charset=utf-8" Method 会自动转为 OPTIONS
实体类在做返回值的时候,会自动序列化为JSON
实体类在做参数的时候,须要将JSON转为字符串,个人处理方式见代码...
而后是折腾好久的麻烦问题:
服务器处理请求时遇到错误。异常消息为“传入消息的消息格式不该为“Raw”。此操做的消息格式应为 'Xml', 'Json'。这多是由于绑定还没有配置 WebContentTypeMapper。
既然是绑定还没有配置,那么须要手动指定一下消息格式为Json,指定的方式是添加下面一个类
public class JsonContentTypeMapper : WebContentTypeMapper { public override WebContentFormat GetMessageFormatForContentType(string contentType) { if (contentType == "application/x-www-form-urlencoded; charset=UTF-8") { return WebContentFormat.Json; } else { return WebContentFormat.Raw; } return WebContentFormat.Json; } }
而后,因为我是用代码方式发布服务,因此应该把他加入到WebHttpEndpoint 中:
webHttpEndPoint.ContentTypeMapper = new JsonContentTypeMapper();
再次测试就OK了
2 跨域调用的问题
你们都知道,处于安全考虑,浏览器是不容许JS脚本跨域调用的。可是做为一个内网的系统,暂时就无论那么多了,大不了再加入身份验证,先解决有无问题,让领导早点看到成果对不对?!
因而我找了不少网站都没有简单的用POST方式能解决的方案,后来在CORS的网站上找到了,可是很是抱歉..网站域名我给忘记了.. 找到的代码以下:
public class CustomHeaderMessageInspector : IDispatchMessageInspector { Dictionary<string, string> requiredHeaders; public CustomHeaderMessageInspector(Dictionary<string, string> headers) { requiredHeaders = headers ?? new Dictionary<string, string>(); } public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { return null; } public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty; foreach (var item in requiredHeaders) { httpHeader.Headers.Add(item.Key, item.Value); } } } public class EnableCrossOriginResourceSharingBehavior : BehaviorExtensionElement, IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) { var requiredHeaders = new Dictionary<string, string>(); requiredHeaders.Add("Access-Control-Allow-Origin", "*"); requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS"); requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type"); endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders)); } public void Validate(ServiceEndpoint endpoint) { } public override Type BehaviorType { get { return typeof(EnableCrossOriginResourceSharingBehavior); } } protected override object CreateBehavior() { return new EnableCrossOriginResourceSharingBehavior(); } }
最后一样要添加到服务的webHttpEndPoint上
EnableCrossOriginResourceSharingBehavior crossOriginBehavior = new EnableCrossOriginResourceSharingBehavior(); webHttpEndPoint.Behaviors.Add(crossOriginBehavior);
至此,大坑和部分小坑已经都搞定了,还有身份验证的问题,不过这个不影响给领导看DEMO,先过个愉快的周末再说!