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(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 = "<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实现跨域将是不可替代的方案。

本文来自:http://www.cnblogs.com/CreateMyself/p/4836628.html

webapi 跨域访问设置基于jsonp跨域的更多相关文章

  1. SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域

    SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域 >>>>>>>>>>>> ...

  2. SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域[转]

    SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域 原文地址:https://www.cnblogs.com/fanshuyao/p/716847 ...

  3. 什么是跨域访问,JSON&amp;JSONP

    http://blog.csdn.net/notechsolution/article/details/50394391 更详细的讲解,关于同源策略等: http://www.cnblogs.com/ ...

  4. ajax跨域访问http服务--jsonp

    在前面一篇文章<Spring Cloud 前后端分离后引起的跨域访问解决方案>里我们提到使用ajax跨域请求其他应用的http服务,使用的是后台增加注解@CrossOrigin或者增加Co ...

  5. [转] Chrome - 浏览器跨域访问设置(附:新老版本两种设置方法)

    [From] http://www.hangge.com/blog/cache/detail_1703.html 在进行前后分离的 webapp 开发,或者 H5 移动 App 开发时,我们会使用 P ...

  6. IIS下设置跨域访问问题--Access-Control-Allow-Origin 站点跨域请求的问题

    背景: 最近 开发中遇到新需求,把公司的OA系统迁移一套到小程序上面去 有些功能的信息是在小程序 查看 但是文件是在pc端上传的 例如:领导在外出办公 使用小程序查看xxxx.pdf文件  这个时候就 ...

  7. 跨域详解之-----Jsonp跨域

    一.通过jsonp跨域 在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的. 比如 ...

  8. asp.net core 系列之允许跨域访问2之测试跨域(Enable Cross-Origin Requests:CORS)

    这一节主要讲如何测试跨域问题 你可以直接在官网下载示例代码,也可以自己写,我这里直接使用官网样例进行演示 样例代码下载: Cors 一.提供服务方,这里使用的是API 1.创建一个API项目.或者直接 ...

  9. 跨域 (1) jsonp 跨域

    jsonp 的例子 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

随机推荐

  1. 精通css 高级web标准解决方案——可视化格式模型-盒模型

    1-盒模型的两种标准: IE :width 和 height属性 是包括padding和border在内的. w3c:width 和 height 属性,就是单纯的内容的宽高,padding 是内容之 ...

  2. cisco tftp 备份/恢复

    使用tftp服务器对cisco 3560 配置备份及恢复 Switch#copy running-config tftp:Address or name of remote host []? 192. ...

  3. 关于后缀数组的倍增算法和height数组

    自己看着大牛的论文学了一下后缀数组,看了好久好久,想了好久好久才懂了一点点皮毛TAT 然后就去刷传说中的后缀数组神题,poj3693是进化版的,需要那个相同情况下字典序最小,搞这个搞了超久的说. 先简 ...

  4. npm 模块化管理

    我们要使用 npm 进行包管理,首先我们得安装 npm,怎么安装?其实只要你安装了 node,那便相当于安装了 npm: node下载地址:http://nodejs.cn/download/ 由于 ...

  5. Kafka Frequently Asked Questions

    This is intended to be an easy to understand FAQ on the topic of Kafka. One part is for beginners, o ...

  6. WPF中的逻辑树和可视化树

    WPF中的逻辑树是指XAML元素级别的嵌套关系,逻辑树中的节点对应着XAML中的元素. 为了方便地自定义控件模板,WPF在逻辑树的基础上进一步细化,形成了一个“可视化树(Visual Tree)”,树 ...

  7. 阿里云上部署kafka--遇到的坑

    阿里云防火墙关闭,并且配置规则.不然会导致访问不到服务. 问题一: Caused by: java.net.UnknownHostException: iZuf68tztea6l5ccdz7wemZ: ...

  8. 11.PowerSploit攻击指南

    本人小白,写这篇文章主要记录一下自己的PowerShell学习之路,大牛请绕道:https://www.anquanke.com/subject/id/90541 首先PowerShell的攻击工具有 ...

  9. Java 环境变量设置 -- JAVA_HOME CLASSPATH

    1.打开我的电脑--属性--高级--环境变量 2.新建系统变量JAVA_HOME 和CLASSPATH 变量名:JAVA_HOME 变量值:C:\Program Files\Java\jdk1.7.0 ...

  10. htmlunit最具有参考意义项目

    ### HtmlUnit What? - 项目1 https://gitee.com/dgwcode/spiderTmallTradeInfo - 项目2 https://gitee.com/dgwc ...