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,先过个愉快的周末再说!

相关文章
相关标签/搜索