文档章节

WCF RESTful 服务+Jquery 客户端 跨域调用 大坑及解决方案汇总

Ghostab
 Ghostab
发布于 2014/11/14 17:39
字数 1180
阅读 501
收藏 2
点赞 0
评论 2

前言

         微软宣布.NET开源,于是我等夹着尾巴混迹OSC的C#程序员终于可以昂首挺胸做人了!

         不知道为何,很多人痛恨.微软的产品,其中不乏没怎么用过C#就开始黑的,只有用过才知道,微软的框架搞出问题来,找解决方案是个极为痛苦的过程,最近几天搞WCF RESTful 服务,以及使用Jquery 跨域调用,出来一系列问题,为了找解决方案,英语阅读能力直线上升,已经到了不看英文文档不舒服的程度!

         由于踩坑太多,感觉又像玩扫雷,又像打地鼠,为了防止以后继续踩坑,赶紧把遇到的一些印象深刻的问题摘录下来。

         目前这项目服务端使用 EF 6.0  WCF 4.0  ,服务端做了个动态创建服务,动态替换服务实现类DLL的机制,然后服务发布起来后出现了一系列问题:

  1 实体类作参数和返回值时,序列化和反序列化出错

        假如有这样一个实体类

       

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

© 著作权归作者所有

共有 人打赏支持
Ghostab

Ghostab

粉丝 5
博文 3
码字总数 2143
作品 0
高级程序员
加载中

评论(2)

joseph7
joseph7
您好,可以发个demo吗,谢谢。邮箱:qinfeng6@126.com
zhu_dk
zhu_dk
您好,可以吧demo源码发我一份们,我也遇到这个问题了,但是我是改的配置文件,不知道用代码怎么配置,我邮箱:zhudaok@163.com
Ajax 跨域难题 - 原生 JS 和 jQuery 的实现对比

讲解顺序: AJAX 的概念及由来 JS 和 jQuery 中的 ajax 浏览器机制 AJAX 跨域 AJAX 的概念 在讲解 AJAX 的概念之前,我先提一个问题。 这是一个典型的 B/S 模式。 PS. B/S结构(Browser/Ser...

梁音 ⋅ 06/19 ⋅ 0

来学着写自己的“jQuery”

jQuery是一套跨浏览器的JavaScript库,简化HTML与JavaScript之间的操作 jQuery是开源软件,使用MIT许可证授权。jQuery的语法设计使得许多操作变得容易,如操作文档对象(document)、选择文档...

YyzclYang ⋅ 05/24 ⋅ 0

jQuery函数attr()和prop()的区别

在jQuery中,attr()函数和prop()函数都用于设置或获取指定的属性,它们的参数和用法也几乎完全相同。 但不得不说的是,这两个函数的用处却并不相同。下面我们来详细介绍这两个函数之间的区别...

Apirl ⋅ 05/16 ⋅ 0

Nginx 解决浏览器 Ajax 跨域问题

跨域是指 host 为 A 页面中的 Ajax 发起指向 host B 的请求,只要 A 和 B 的协议、域名、端口、子域名其中任何一项不同,则执行的访问都会被认为是跨域的请求,几乎所有的浏览器为了安全等问...

摆码王子 ⋅ 04/16 ⋅ 0

JavaScript异步精讲,让你更加明白Js的执行流程!

JavaScript异步精讲,让你更加明白Js的执行流程! 问题点 什么是单线程,和异步有什么关系 什么是 event-loop jQuery的Deferred Promise 的基本使用和原理 async/await(和 Promise的区别、联...

推荐码发放 ⋅ 05/28 ⋅ 0

php中ajax跨域请求---小记

php中ajax跨域请求---小记 前端时间,遇到的一个问题,情况大约是这样: 原来的写法: 前端js文件中: $.ajax({   type:'get',   url:'http://wan.xxx.com/xxx.js',   success:functio...

thinkyoung ⋅ 2015/08/31 ⋅ 0

Python自动化开发学习25-Django

组合搜索 下面要讲的是基于模板语言的实现方法,完全没有使用js。讲的时候有点混乱,把其他与效果实现无关的小知识点也顺带讲了一下。不过我最后做了小结。 准备表结构 这里讲组合搜索,所以...

骑士救兵 ⋅ 前天 ⋅ 0

jQuery之validate验证表单

访问jQuery validate官网下载最新插件 https://jqueryvalidation.org/ validate是用来验证表单的 以前我们都是用js手动验证 现在可以通过这个插件直接调用别人写好的方法 更加简单方便 vali...

codingcoge ⋅ 05/19 ⋅ 0

Jquery实现京东tab切图

主要是用jquery实现tab切换,显示不同的内容。也可以用原生js实现,但是实现方式比较繁琐,这里暂时不写原生js实现。jquery相对代码少而且易于理解。 这里用了三种方式实装,在写的时候,发现...

Mrs_CoCo ⋅ 05/07 ⋅ 0

Python自动化开发学习17-jQuery

jQuery学习之前 jQuery 是 JavaScript 的一个类库,类似 python 中的模块。 jQuery在线手册:http://jquery.cuishifeng.cn/ 官网:http://jquery.com/ 版本选择 目前jQuery有三个大版本:1.x...

骑士救兵 ⋅ 06/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

使用 vue-cli 搭建项目

vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli 一、 安装 node.js 首先需要安装node环境,可以直接到中...

初学者的优化 ⋅ 7分钟前 ⋅ 0

设计模式 之 享元模式

设计模式 之 享元模式 定义 使用共享技术来有效地支持大量细粒度对象的复用 关键点:防止类多次创建,造成内存溢出; 使用享元模式来将内部状态与外部状态进行分离,在循环创建对象的环境下,...

GMarshal ⋅ 23分钟前 ⋅ 0

SpringBoot集成Druid的最简单的小示例

参考网页 https://blog.csdn.net/king_is_everyone/article/details/53098350 建立maven工程 Pom文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM......

karma123 ⋅ 今天 ⋅ 0

Java虚拟机基本结构的简单记忆

Java堆:一般是放置实例化的对象的地方,堆分新生代和老年代空间,不断未被回收的对象越老,被放入老年代空间。分配最大堆空间:-Xmx 分配初始堆空间:-Xms,分配新生代空间:-Xmn,新生代的大小一...

算法之名 ⋅ 今天 ⋅ 0

OSChina 周日乱弹 —— 这么好的姑娘都不要了啊

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @TigaPile :分享曾惜的单曲《讲真的》 《讲真的》- 曾惜 手机党少年们想听歌,请使劲儿戳(这里) @首席搬砖工程师 :怎样约女孩子出来吃饭,...

小小编辑 ⋅ 今天 ⋅ 8

Jenkins实践3 之脚本

#!/bin/sh# export PROJ_PATH=项目路径# export TOMCAT_PATH=tomcat路径killTomcat(){pid=`ps -ef | grep tomcat | grep java|awk '{print $2}'`echo "tom...

晨猫 ⋅ 今天 ⋅ 0

Spring Bean的生命周期

前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: Spring 只帮我们管理单例模...

素雷 ⋅ 今天 ⋅ 0

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 今天 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部