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":"4836628","Name":"xpy0928"},{......}])
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作为调用函数的参数 }
};
以上是利用原生的JS实现,但是在JQuery中却对此进行了封装,如下:
$.getJSON("http://www.cnblogs.com/CreateMyself/WebAPI/xpy0928?callback=?",function(jsonData){ })
上述callback=?,对于callback中的?而言,JQuery会自动生成我们上述手动创建的全局处理函数,并在调用完函数之后自动销毁,毫无疑问该回调函数就类似于JS中的代理对象,也就是所谓的临时代理函数,同时JQuery也会自动去检测该请求是否是跨域请求,若不是,则以普通Ajax进行请求,否则则以异步加载JS文件的形式来执行JSONP中的回调函数。
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;
}
}
第二步
此时只需将此自定义类在Web API配置文件中进行注册即可:
GlobalConfiguration
.Configuration
.Formatters
.Insert(, 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 =, Gender="男"},
new Person{ Name="xpy0929", Age =, Gender="女"},
new Person{ Name="xpy0930", Age =, Gender="男"},
};
return Person;
}
接下来就是进行验证了。调用上述前台所写的JSONP方法:
function getPerson() {
JSONP("http://localhost:23133/api/default?callback=?",
function (persons) {
$.each(persons, function (index, person) {
var html = "<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 = "<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实现跨域将是不可替代的方案。
Web APi之手动实现JSONP或安装配置Cors跨域(七)的更多相关文章
- Web API 实现JSONP或者安装配置Cors跨域
前言 照理来说本节也应该讲Web API原理,目前已经探讨完了比较底层的Web API消息处理管道以及Web Host寄宿管道,接下来应该要触及控制器.Action方法,以及过滤器.模型绑定等等,想想 ...
- Web Api之Cors跨域以及其他跨域方式(三)
我们知道ajax不能跨域访问,但是有时我们确实需要跨域访问获取数据,所以JSONP就此诞生了,其本质使用的是Script标签,除JSONP以外还有另外实现跨域方式 一.手动实现JSONP跨域 1.首先 ...
- 在ASP.NET Web API中实现CORS(跨域资源共享)
默认情况下,是不允许网页从不同的域访问服务器资源的,访问遵循"同源"策略的原则. 会遇到如下的报错: XMLHttpRequest cannot load http://local ...
- Web Api之Cors跨域(干货)---大家一定要看清我写的内容哦
Web Api之Cors跨域 要想跨域需要准备一下几步骤 1.创建WebAPI(请按照图片先后顺序来) 2.进入NuGet包管理搜 Microsoft.AspNet.WebApi.Cors 进行下载 ...
- jsonp与cors跨域的一些理解(转)
CORS其实出现时间不短了,它在维基百科上的定义是:跨域资源共享(CORS )是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源.而这种访问是被同源策略所禁止的. ...
- Api之Cors跨域以及其他跨域方式
Web Api之Cors跨域以及其他跨域方式(三) 我们知道ajax不能跨域访问,但是有时我们确实需要跨域访问获取数据,所以JSONP就此诞生了,其本质使用的是Script标签,除JSONP以外还 ...
- jsonp与cors跨域的一些理解
浏览器的同源策略,即是浏览器之间要隔离不同域的内容,禁止互相操作. 比如,当你打开了多个网站,如果允许多个网站之间互相操作,那么其中一个木马网站就可以通过这种互相操作进行一系列的非法行为,获取你在各个 ...
- 【从零开始搭建自己的.NET Core Api框架】(五)由浅入深详解CORS跨域机制并快速实现
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- Spring Boot Web应用开发 CORS 跨域请求支持:
Spring Boot Web应用开发 CORS 跨域请求支持: 一.Web开发经常会遇到跨域问题,解决方案有:jsonp,iframe,CORS等等CORS与JSONP相比 1. JSONP只能实现 ...
随机推荐
- C#输出文本树形层次,前或者后自定义空格位数
Indent String with Spaces This example shows how to indent strings using method for padding in C#. T ...
- MSSQL数据库表加锁
所指定的表级锁定提示有如下几种: 1. HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放所添加的锁. 2. NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可 ...
- struts2 框架处理流程
struts2 框架处理流程 流程图如下: 注意:StrutsPrepareAndExecuteFilter替代了2.1.3以前的FilterDispatcher过滤器,使得在执行Action之前可以 ...
- iOS 中 ARC 项目 兼容 MRC
iOS 项目中MRC 和 ARC 项目的代码兼容问题: 1.ARC 项目中导入 MRC 第三方类的时候要在此类上添加 -objc-arc. 2.MRC 项目中导入 ARC 类的时候要在次类上添加 -f ...
- [Android] 时间Time Date 以及Location中gettime
import android.text.format.Time; 还有一个是Date Location中的gettime, 这几个每个默认的格式都不一样,直接输出字符串各自得到了不同 比如按照获取当前 ...
- DSY3163*Eden的新背包问题
Description "寄没有地址的信,这样的情绪有种距离,你放着谁的歌曲,是怎样的心心静,能不能说给我听."失忆的Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的 ...
- 验证mongodb副本集并实现自动切换primary~记录过程
接 验证mongodb主从复制过程 1.创建数据目录 同 验证mongodb主从复制过程 的实验一样,本次实验也是采用直接指定启动参数来启动mongodb数据库,本次实验我们需要启动三个数据库,为了与 ...
- (转载)H.264码流的RTP封包说明
H.264的NALU,RTP封包说明(转自牛人) 2010-06-30 16:28 H.264 RTP payload 格式 H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) ...
- linux命令初识
一.查看当前的目录文件 ls demo 查看demo目录下的所有文件 ls -l demo/test.txt --查看指定目录(test.txt)的详细内容 二.复制文件 cp or ...
- JS简介,如何快熟JS。打下结实基础。
JS决定网页的行为,有行为就有逻辑,而计算机只是人工智能,你给它什么样的指令,它就会按照你的指令去运行. 有人会问,既然是给出指令,那还需要什么逻辑? 这里我举一个简单的小例子来说明一下! 到你想输入 ...