WCF RESTful 服務+Jquery 客戶端 跨域調用 大坑及解決方案彙總

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

相關文章
相關標籤/搜索