前言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,先過個愉快的週末再說!