Web API 实现JSONP或者安装配置Cors跨域

简介:

前言

照理来说本节也应该讲Web API原理,目前已经探讨完了比较底层的Web API消息处理管道以及Web Host寄宿管道,接下来应该要触及控制器、Action方法,以及过滤器、模型绑定等等,想想也是心痛不已,水太深了,摸索原理关键是太枯燥和乏味了,但是呢,从情感上还是挺乐意去摸索原理,而情绪上不太乐意去探究原理,于是乎,本文就由此诞生了,借此文缓解下枯燥的心情和压抑的情绪。后续继续摸索原理。

接下来我们要讲的就是利用JSONP和利用Cors这两种方式来实现跨域,请看下文。。。。。

JSONP实现跨域

Web API并没有提供JSONP  Formatter,但是这并不能影响我们前进的脚步,我们可以自定义Formatter来实现JSONP功能。既然是利用JSONP跨域,那么就得简单介绍下JSONP。

为什么需要JSONP?

浏览器都是基于同源策略,使其脚本不能跨站点来获得服务器端数据,但是办法总是人想出来的,这个时候就需要JSONP了,当然也可以用别的办法实现,JSONP是一种能实现让基于JavaScript的客户端程序绕过跨站点脚本的限制从而从非当前的服务器上来获得数据的方式。默认情况下,应用程序利用Ajax是不允许访问远程跨域,但是我们可以利用<script>标签加载JSONP来实现这种跨站点限制。这也不失为一种好的解决方案。JSONP的工作原理是当JSON数据返回时通过组合JSON数据,并将其包裹到一个函数中进行调用,利用JQuery更能很好的去实现这点。

假如有这样如下的一个URL:

http://www.cnblogs.com/CreateMyself/WebAPI/xpy0928

但我们利用Ajax发出GET请求来获取服务器端数据时那将是轻而易举,但是,但是,但是,重要的前提说三遍,前提是在相同域下,若是不同的域下,利用Ajax来访问数据估计不是这么轻松了吧。但是,但是,但是,重要的话再说三遍,此时我们就利用JSONP来实现跨域,此时将会变成如下请求模式:

http://www.cnblogs.com/CreateMyself/WebAPI/xpy0928?callback=?

发出如下URL请求通过一个callback回调,这样得到的结果是和同一站点的结果是一致的,JQuery会反序列会这些数据并将其推入到函数中。

JSONP数据是怎样的?

它主要就是通过调用函数将返回的JSON数据进行包裹,类似于如下形式:

Query7d59824917124eeb85e5872d0a4e7e5d([{"Id":"123","Name":"xoy0928"},{......}])

JSONP的工作原理是怎样的呢?

在JavaScript客户端发出请求后,当响应数据时,将其数据作为执行要调用函数的参数,并在其内部将JSON数据进行反序列化

下面我们就原理来进行演示,请看如下代码:

复制代码
复制代码
    function JSONP(url, callback) {
        var id = "_" + "Query" + (new Date()).getTime();  //创建一个几乎唯一的id
        
        window[id] = function (result) {  //创建一个全局回调处理函数

            if (callback)
                callback(result);

            var getId = document.getElementById(id);  //移除Script标签和id
            getId.parentNode.removeChild(getId);
            window[getId] = null;
        }

        url = url.replace("callback=?", "callback=" + id);

        var script = document.createElement("script");  //创建Script标签并执行window[id]函数
        script.setAttribute("id", id);
        script.setAttribute("src", url);
        script.setAttribute("type", "text/javascript");
        document.body.appendChild(script);
    }
复制代码
复制代码

简单进行调用则如下:

复制代码
复制代码
function JSONPFunction() {
        
        JSONP("http://localhost:23133/api/default?callback=?",
        
        function(jsonData){          //将返回的数据jsonData作为调用函数的参数

        }
};
复制代码
复制代码

JSONP在Web API中如何实现呢? 

上述讲了JSONP原理和实现,那么结合Web API是如何实现的呢?我们只能自定义Formatter来手动实现这个功能,既然是有关于JSON,那么自然是继承于 JsonMediaypeFormatter 了,代码如下:

第一步

自定义JsonpFormatter并继承于JsonMediaTypeFormatter:

复制代码
复制代码
    public class JsonpFormatter : JsonMediaTypeFormatter
    {
     //当请求过来是带有text/javascript时处理JSONP请求
        public JsonpFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

            JsonpParameterName = "callback";
        }
//查找函数名 public string JsonpParameterName { get; set; } private string JsonpCallbackFunction; public override bool CanWriteType(Type type) { return true; }
//重写此方法来捕获请求对象 public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType) { var formatter = new JsonpFormatter() { JsonpCallbackFunction = GetJsonCallbackFunction(request) };
//运用JSON.NET来序列化自定义 formatter.SerializerSettings.Converters.Add(new StringEnumConverter()); formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; return formatter; } //重写此方法写入到流并返回 public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext) { if (string.IsNullOrEmpty(JsonpCallbackFunction)) return base.WriteToStreamAsync(type, value, stream, content, transportContext); StreamWriter writer = null; try { writer = new StreamWriter(stream); writer.Write(JsonpCallbackFunction + "("); writer.Flush(); } catch (Exception ex) { try { if (writer != null) writer.Dispose(); } catch { } var tcs = new TaskCompletionSource<object>(); tcs.SetException(ex); return tcs.Task; } return base.WriteToStreamAsync(type, value, stream, content, transportContext) .ContinueWith(innerTask => { if (innerTask.Status == TaskStatus.RanToCompletion) { writer.Write(")"); writer.Flush(); } }, TaskContinuationOptions.ExecuteSynchronously) .ContinueWith(innerTask => { writer.Dispose(); return innerTask; }, TaskContinuationOptions.ExecuteSynchronously) .Unwrap(); }
//从查询字符串中获得JSONP Callback回调函数 private string GetJsonCallbackFunction(HttpRequestMessage request) { if (request.Method != HttpMethod.Get) return null; var query = HttpUtility.ParseQueryString(request.RequestUri.Query); var queryVal = query[this.JsonpParameterName]; if (string.IsNullOrEmpty(queryVal)) return null; return queryVal; } }
复制代码
复制代码

第二步

此时应将此自定义类进行注册即可:

            GlobalConfiguration
                .Configuration
                .Formatters
                .Insert(0, new JsonpFormatter());

第三步

给出后台测试数据:

复制代码
复制代码
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Gender { get; set; }
    }


        public IEnumerable<Person> GetAllPerson()
        {

            Person[] Person = new Person[]
            {
                new Person{ Name="xpy0928", Age =11, Gender="男"},
                new Person{ Name="xpy0929", Age =12, Gender="女"},
                new Person{ Name="xpy0930", Age =13, Gender="男"},
           };
            return Person;
        }
复制代码
复制代码

接下来就是进行验证了。调用上述前台所写的JSONP方法:

复制代码
复制代码
     function getPerson() {
        JSONP("http://localhost:23133/api/default?callback=?",
              function (persons) {
                  $.each(persons, function (index, person) {
                      var html = "<li><ul>";
                      html += "<li>Name: " + person.Name + "</li>";
                      html += "<li>Age:" + person.Age + "</li>";
                      html += "<li>Gender: " + person.Gender + "</li>";
                      html += "</ul>";
                      $("#person").append($(html));
                  });
              });
    };
    $(function () {
        $("#btn").click(function () {
            getPerson();
        });
    });
复制代码
复制代码

上述也可自行利用Ajax来请求,以下几项必不可少:

复制代码
复制代码
        $.ajax({
                type: "Get",
                url: "http://localhost:23133/api/default/?callback=?",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
.......
})
复制代码
复制代码

点击加载数据:

<input type="button" value="获取数据" id="btn" />
<ul id="person"></ul>

既然是跨站点就开两个应用程序就得了呗,服务器端:localhost:23133,客户端:localhost:29199,走你,完事:

总结

一切圆满结束,似乎利用JSONP实现跨域是个不错的解决方案,但是有的人就问了,JSONP也有局限性啊,只能针对于Get请求不能用于POST请求啊,并且还需要手动去写这么操蛋的代码,有点令人发指,恩,是的,确实是个问题,你想到的同时我也替你想到了,请看下文!

Cors实现跨域

使用Cors跨域配置是极其的简单,但是前提是你得通过NuGet下载程序包,搜索程序包【Microsoft.AspNet.WebApi.Cors】即可,如图:

下载完成后,有两种配置跨域的方式

第一

在Web API配置文件中进行全局配置:

            var cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);

第二

若你仅仅只是想某个控制器应用跨域也就是说实现局部控制器跨域,当然你也可以通过添加特性来实现这点:

   [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class HomeController : Controller
    {

    }

尝试(一)

在被请求的服务器端的Web API配置文件中,进行全文配置,接下来发出POST请求如下:

复制代码
复制代码
        $("#btn").click(function () {
            $.ajax({
                type: "POST",
                url: "http://localhost:23133/api/Default/PostAllPerson",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                cache: false,
                success: function (persons) {
                    $.each(persons, function (index, person) {
                        var html = "<li><ul>";
                        html += "<li>Name: " + person.Name + "</li>";
                        html += "<li>Age:" + person.Age + "</li>";
                        html += "<li>Gender: " + person.Gender + "</li>";
                        html += "</ul>";
                        $("#person").append($(html));
                    });
                }
            });
        });
复制代码
复制代码

如我们所期望的一样,测试通过:

尝试(二)

在控制器上进行局部配置,并发出Get请求,修改如下:

复制代码
复制代码
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class DefaultController : ApiController
    {
        public IEnumerable<Person> GetAllPerson()
        {}
    }
复制代码
复制代码

发出请求如下:

复制代码
复制代码
     $.ajax({
                type: "Get",
                url: "http://localhost:23133/api/Default",
                dataType: "json",
                ........
           })
复制代码
复制代码

我们查看其请求报文头信息以及返回状态码便知是否成功,如下(如预期一样):

经测试利用Cors实现对于Get和POST请求都是来者不拒,都能很友好的返回响应的数据并且配置简单。当然Cors的功能远不止如此简单,更多详细信息,请参看【Cors-Origin For WebAPI】 

总结 

利用JSONP能较好的实现在Web API上的跨域,但是有两个严重的缺陷,第一:只能是Get请求。第二:你得自定义实现JsonMediaTypeFormatter。在Cors未出世之前你没有更好的解决方案,你只能忍受,自从Cors出世,我们不再受请求的限制,不再手动去实现,只需稍微配置即可达到我们所需,所以利用Cors实现跨域将是不可替代的方案。

 

转:http://www.cnblogs.com/CreateMyself/p/4836628.html

分类:  ASP.NET, MVC, WebApi
本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/4837216.html ,如需转载请自行联系原作者

相关文章
|
5天前
|
开发框架 JSON API
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
【7月更文挑战第22天】在Python Web开发中,设计高效的RESTful API涉及选择框架(如Flask或Django)、明确资源及使用HTTP方法(GET, POST, PUT, DELETE)来操作数据。响应格式通常是JSON,错误处理也很重要。示例展示了使用Flask创建图书管理API,包括版本控制、文档化、安全性和性能优化是最佳实践。这样的API使数据交互更顺畅。
26 2
|
2天前
|
JSON API 开发者
惊!Python Web开发新纪元,RESTful API设计竟能如此性感撩人?
【7月更文挑战第24天】在 Python Web 开发领域, RESTful API 设计成为一种艺术, 关注用户体验与开发者友好性。
20 7
|
3天前
|
JSON API 数据格式
深度剖析!Python Web 开发中 RESTful API 的每一个细节,你不可不知的秘密!
【7月更文挑战第23天】在Python Web开发中,RESTful API利用HTTP协议构建强大、灵活的应用。GET获取资源,如`/products/:id`;POST创建新资源;PUT更新;DELETE删除。正确使用状态码,如200、201、404、500,至关重要。JSON化数据与版本控制(如`/v1/products`)增强API实用性。认证(OAuth, JWT)保障安全性,而清晰的错误消息提升用户体验。掌握这些细节,方能设计出高性能、易用的RESTful API。
20 7
|
2天前
|
SQL API 网络架构
Pythoner必看!RESTful API设计秘籍,让你的Web应用瞬间高大上!
【7月更文挑战第24天】在 Python Web 开发中, RESTful API 设计至关重要, 它不仅展现技术实力, 更提升应用的可维护性与用户体验。
18 5
|
2天前
|
JSON API 数据库
从零到英雄?一篇文章带你搞定Python Web开发中的RESTful API实现!
【7月更文挑战第24天】在Python的Web开发领域,掌握RESTful API至关重要。利用Flask框架,可迅速搭建API,进行用户管理的CRUD操作。需先安装Flask (`pip install Flask`),然后定义路由处理GET、POST、PUT、DELETE请求,实现用户数据的检索、创建、更新和删除。
19 5
|
4天前
|
API 数据安全/隐私保护 开发者
Web 开发新风尚!Python RESTful API 设计与实现,让你的接口更懂开发者心!
【7月更文挑战第23天】Python的RESTful API设计在Web开发中流行,提升效率与体验。REST强调HTTP方法(GET, POST, PUT, DELETE)操作资源。使用Flask框架可快速实现API,如管理用户信息。示例代码展示如何创建、读取、更新和删除用户,通过不同HTTP方法和URL路径。实际应用中,增加验证、错误处理和权限控制可增强API的安全性和稳定性。安装Flask后,可运行代码测试API功能。
24 6
|
2天前
|
JSON API 网络架构
揭秘!Python Web框架下的RESTful API设计,为何成为互联网新宠?
【7月更文挑战第24天】在互联网快速发展背景下,RESTful API以其简洁性和可扩展性成为应用间通信的首选。REST强调资源为核心,利用HTTP方法处理CRUD操作,促进前后端分离,提高开发效率。Python的Flask框架支持轻松构建RESTful API,通过路由、请求和响应机制实现。示例代码展示了如何使用Flask创建、读取和更新用户信息。Python Web框架因其丰富的工具和库,简化RESTful API开发,适应微服务架构需求,在互联网发展中扮演重要角色。
|
5天前
|
安全 API 网络架构
Python RESTful API设计新篇章,打造高效、易用、安全的Web服务接口,你准备好了吗?
【7月更文挑战第22天】在数字化转型中,RESTful API借助Python的Flask和Django REST Framework,提供高效、易用和安全的接口设计。Flask示例展示了简洁的CRUD操作,Swagger等工具增进API文档的易用性,而HTTPS、JWT和输入验证确保安全性。Python RESTful API设计涉及效率、可用性和安全,是不断进化的Web服务接口的关键。准备好踏上这一新篇章了吗?一起探索,创造卓越!
|
12天前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
【7月更文挑战第15天】AJAX和Fetch API是Web开发中的关键工具,用于异步前后端交互。AJAX借助XMLHttpRequest实现页面局部更新,而Fetch API是现代的、基于Promise的HTTP请求接口,提供更强大功能和简洁语法。结合Python Web框架如Django或Flask,利用这两者能创造无缝体验,提升项目性能和用户体验。学习并应用这些技术,将使你的Web应用焕发新生。**
21 5
|
14天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
【7月更文挑战第13天】在Web开发中,AJAX和Fetch API是实现页面无刷新数据交换的关键。在Flask博客系统中,通过创建获取评论的GET路由,我们可以展示使用AJAX和Fetch API的前端实现。AJAX通过XMLHttpRequest发送请求,处理响应并在成功时更新DOM。Fetch API则使用Promise简化异步操作,代码更现代。这两个工具都能实现不刷新页面查看评论,Fetch API的语法更简洁,错误处理更直观。掌握这些技巧能提升Python Web项目的用户体验和开发效率。
27 7