using MvcApp.Filters; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApp.Controllers { [AllowCors("http://localhost:13433", "http://localhost:13435")] //[AllowCors] public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult Test() { var json = new { Name = "Server" }; return Json(json,JsonRequestBehavior.AllowGet); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApp.Filters { public class AllowCorsAttribute:ActionFilterAttribute { private string[] _domains; public AllowCorsAttribute() { //如果[AllowCors]特性标签没有带域名,那么就从配置文件中读取允许访问的域名,然后将它赋给_domains //_domains = 这里应该是从配置文件中读出来的所有域名组成的数组 _domains = new string[] { "", "" }; } public AllowCorsAttribute(params string[] domain) { _domains = domain; } public override void OnActionExecuting(ActionExecutingContext filterContext) { var urlReferrer = filterContext.HttpContext.Request.UrlReferrer; if (urlReferrer != null) { var absolutePath = urlReferrer.OriginalString; var absolutePathFormat = absolutePath.Substring(0, absolutePath.Length - 1); if (_domains.Contains(absolutePathFormat)) { filterContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", absolutePathFormat); } } else { //如果urlReferrer为空,我理解为自己本地访问(亲自测试,本地访问urlReferrer为null) filterContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*"); } filterContext.HttpContext.Response.AddHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT"); filterContext.HttpContext.Response.AddHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); base.OnActionExecuting(filterContext); } } }
@{ ViewBag.Title = "Index"; } <h2>Index</h2> <script type="text/javascript"> $.ajax({ url: 'http://localhost:24102/Home/Test', type: 'Post', success: function (data, textStatus) { alert(data.Name); }, error:function(XMLHttpRequest, textStatus, errorThrown){ alert(XMLHttpRequest.state); } }) </script>
-------------------------------------------------------------------------------------
JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。
1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;
2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);
3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;
4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;
5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。
namespace MvcApp.Controllers { public class HomeController : Controller { public ActionResult Index() { string callback = this.Request["callback"]; //获取回调函数的名称,的到showMessage string json = "{"name":"server"}"; this.Response.Write(callback + "(" + json + ")"); return new EmptyResult(); } } }
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script> function showMessage(result) { alert(result.name) //运行后打印出server } </script> <script src="http://localhost:58382/home/Index?callback=showMessage" type="text/javascript"></script> </head> <body> <div> </div> </body> </html>
-----------MvcApp项目优化 (优化服务端) 主要是自定义一个JsonpResult类,这个类继承了JsonResult类
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Script.Serialization; namespace MvcApp.Controllers { public class HomeController : Controller { public JsonpResult IndexTest() { return this.Jsonp(new { name = "server JsonpResult" }); } } public class JsonpResult : JsonResult { public static readonly string JsonpCallbackName = "callback"; public static readonly string CallbackApplicationType = "application/json"; public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new AccessViolationException("context"); } if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) //如果不允许来自客户端的Get请求,并且请求类型是Get { throw new AccessViolationException(); } var response = context.HttpContext.Response; if (!string.IsNullOrEmpty(ContentType)) //这个ContentType是获取或设置内容的类型。它是JsonResult类中的一个属性 { response.ContentType = ContentType; //设置响应内容的类型 } else { response.ContentType = CallbackApplicationType; //设置响应内容的类型 } if (ContentEncoding != null) { response.ContentEncoding = this.ContentEncoding;//设置响应内容的编码 } if (Data != null) //这个Data是JsonResult类中的一个属性 { string buffer; var request = context.HttpContext.Request; var serializer = new JavaScriptSerializer(); if (request[JsonpCallbackName] != null) //获取回调函数名称(即:获取这个<script src="http://localhost:58382/home/IndexTest?callback=showMessage" type="text/javascript"></script>链接上的callback的值,即获取showMessage) { buffer = String.Format("{0}({1})", request[JsonpCallbackName], serializer.Serialize(Data));//首先根据callback获取获取函数名,然后传入json字符串作为函数参数 } else { buffer = serializer.Serialize(Data); } response.Write(buffer); } } } /// <summary> /// Controller控制器类的扩展方法,即:Controller控制器下扩展一个Jsonp方法,这个方法带一个object类型的参数 /// </summary> public static class ControllerExtension { public static JsonpResult Jsonp(this Controller controller, object data) { JsonpResult result = new JsonpResult() { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; return result; } } }
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script> function showMessage(result) { alert(result.name) //打印出:server JsonpResult } </script> <script src="http://localhost:58382/home/IndexTest?callback=showMessage" type="text/javascript"></script> </head> <body> <div> </div> </body> </html
案例:二
直接通过ajax进行跨域请求数据(将dataType的值设为jsonp)
这种方式其中本质也是和案例一是一样的。都是通过Get请求
它最终也是通过请求 http://localhost:58382/home/Index?callback=jsonpCallback&_=1481277739025 这个地址,然后服务端拿到callback这个回调函数,然后返回 jsonpCallback({"name":"Server"}) 这样的一个函数,这样我们在本地定义的jsonpCallback函数就可以使用了。这样功能跨域就实现了
<html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src="~/Scripts/jquery-1.8.2.js"></script> </head> <body> <div> </div> </body> </html> <script type="text/javascript"> $.ajax({ url: 'http://localhost:58382/home/Index', //请求的url dataType: "jsonp", //将请求类型设为值jsonp //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonp: "callback", //服务端会通过HttpContext.Request["callback"]; 拿到jsonpCallback这个回调函数的名称 jsonpCallback: "jsonpCallback", //自定义的jsonp回调函数名称"jsonpCallback",返回的json也必须有这个函数名称 success: function (json) { console.log(json); alert(json.name); }, error: function (xhr, status, error) { console.log(xhr); } }); </script>
<pre snippet_file_name="blog_20161209_5_7492657" code_snippet_id="2039637"></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre> <pre></pre>