前言

照理来说本节也应该讲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

Web API 实现JSONP或者安装配置Cors跨域的更多相关文章

  1. Web APi之手动实现JSONP或安装配置Cors跨域(七)

    前言 照理来说本节也应该讲Web API原理,目前已经探讨完了比较底层的Web API消息处理管道以及Web Host寄宿管道,接下来应该要触及控制器.Action方法,以及过滤器.模型绑定等等,想想 ...

  2. Flask配置Cors跨域

    1 跨域的理解 跨域是指:浏览器A从服务器B获取的静态资源,包括Html.Css.Js,然后在Js中通过Ajax访问C服务器的静态资源或请求.即:浏览器A从B服务器拿的资源,资源中想访问服务器C的资源 ...

  3. SpringBoot2.x配置Cors跨域

    1 跨域的理解 跨域是指:浏览器A从服务器B获取的静态资源,包括Html.Css.Js,然后在Js中通过Ajax访问C服务器的静态资源或请求.即:浏览器A从B服务器拿的资源,资源中想访问服务器C的资源 ...

  4. SpringBoot配置Cors跨域请求

    一.同源策略简介 同源策略[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源. 同源策略是浏览器安全的基石. 什么是源 源[or ...

  5. 改变mvc web api 支持android ,ios ,ajax等方式跨域调用

    公司一个移动后端的项目用到了 webapi 项目搭建到外网环境共app开发者调用测试接口时遇到了一个问题 接口不允许跨域调用 .查阅资料明白 同源策略原则根据请求报头值 Origin 与回应报头值 A ...

  6. GeoServer配置CORS(跨域资源共享)

    当前台页面请求WMS可能会遇到浏览器以下提示(浏览器控制台): 已阻止跨源请求:同源策略禁止读取位于 http://xxx.xxx.com 的远程资源.(原因:CORS 头缺少 'Access-Con ...

  7. egg.js 配置cors跨域

    1.egg简述 Egg.js,为企业级框架和应用而生,是阿里开源的企业级 Node.js 框架. 2.特点 Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,团队内部采用这种方式可以减少开 ...

  8. Asp.Net Core 3.0 学习3、Web Api 文件上传 Ajax请求以及跨域问题

    1.创建Api项目 我用的是VS2019 Core3.1 .打开Vs2019 创建Asp.Net Core Web应用程序命名CoreWebApi 创建选择API 在Controller文件夹下面添加 ...

  9. tomcat7.0配置CORS(跨域资源共享)

    平时我们做前台页面时可能会遇到浏览器以下提示(浏览器控制台): 已阻止跨源请求:同源策略禁止读取位于 http://xxx.xxx.com 的远程资源.(原因:CORS 头缺少 'Access-Con ...

随机推荐

  1. Struts2(十二)使用验证框架验证数据较验

    一.数据验证 1.1.为什么要进行数据验证 对数据的合法性进行检查,只允许合法的数据进入应用程序 1.2.在哪里实现数据验证 客户端验证: 数据提交前在客户端验证 可使用JavaScript或者JQu ...

  2. 【原/转】iOS中非常强大的过滤器:NSPredicate

    在APPLE的官方Demo:UICatalog中实现UISearchBar模糊搜索功能是这么做的: - (void)viewDidLoad { [super viewDidLoad]; self.al ...

  3. 【原】画流程图工具visio使用技巧汇总

    最近写论文需要画不少流程图,有两种选择,一是word, 二是visio.原先一直用word画,效果也还可以,但是每个部件画完后为了便于适应排版变动,需要将需要的模块按下ctrl逐个点击选中后进行组合. ...

  4. Python yield 使用浅析(iterable generator )

    http://blog.csdn.net/preterhuman_peak/article/details/40615201 如何生成斐波那契數列 斐波那契(Fibonacci)數列是一个非常简单的递 ...

  5. SAM4E单片机之旅——20、DMAC之使用Multi-buffer进行内存拷贝

    这次使用这个DMAC的Multi-buffer传输功能,将两个缓冲区的内容拷贝至一个连续的缓冲区中. 一. DMAC 在M4中,DMA控制器(DMAC)比外设DMA控制器(PDC)要复杂,但是功能更加 ...

  6. 【linux】关于分析系统问题的前几分钟

    为了解决性能问题,你登入了一台Linux服务器,在最开始的一分钟内需要查看什么?你可以在几分钟内就对系统资源的使用情况和进程的运行状况有大体上的了解.无非是先查看错误信息和饱和指标,再看下资源的使用量 ...

  7. oracle序列

    一.序列 序列是oracle用来生产一组等间隔的数值.序列是递增,而且连续的.oracle主键没有自增类型,所以一般使用序列产生的值作为某张表的主键,实现主键自增.序列的编号不是在插入记录的时候自动生 ...

  8. spring mvc 配置文件拦截器过滤url

    最近在用spring mvc拦截器,sprin 版本号4.0.6.RELEASE, <mvc:interceptor> <mvc:mapping path="/admin/ ...

  9. 利用API方式进行数据库的增删改查

    /* 将数据库的增删改查单独放进一个包 */ package com.itheima28.sqlitedemo.dao; import java.util.ArrayList; import java ...

  10. django模型

    用django时,只要用到数据库就得用到模型. 一.数据库的MTV开发模式 从MVC到MTV 所谓软件架构的MVC模式将数据的存取逻辑(Module),表现逻辑(View)和业务逻辑(Controll ...