坐在海边看星星

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1:原理

在js中,XMLHttpRequest是不能请求不同域的数据,但是script标签却可以,所以可以用script标签实现跨域请求。具体是定义一个函数,例如jsonp1234,请求不同域的url时带上函数名,例如:http://otherdomain.com/index?callback=jsonp1234,然后服务端根据callback获取这个函数名,然后传入json字符串作为函数参数。

2:实现

http://localhost:62203/home/index页面代码如下

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script>
    function showMessage(result) {
        alert(result.name)
    }
    </script>
    <script src="http://localhost:16308/home/index?callback=showMessage" type="text/javascript"></script>
</head>
<body>
    <div> 
    </div>
</body>
</html>

  

主要是这句  

<script src="http://localhost:16308/home/index?callback=showMessage" type="text/javascript"></script>,

可以看到,访问的是不同的站点,并且callback的参数值为showMessage,

http://localhost:16308/home/index代码如下

public ActionResult Index()
        {
            string callback = this.Request["callback"];
            string json="{\"name\":\"server\"}";
            this.Response.Write(callback + "(" + json + ")");
            return new EmptyResult();
        }

  

根据callback获取函数名,然后将json字符串作为函数参数。

访问页面http://localhost:62203/home/index,效果如下

可见,站点localhost:62203从站点localhost:16308获取到了数据。 

但是我们看服务端的实现,这也太不美观,也比较麻烦。

public ActionResult Index()
        {
            string callback = this.Request["callback"];
            string json="{\"name\":\"server\"}";
            this.Response.Write(callback + "(" + json + ")");
            return new EmptyResult();
        }

  我们想要的是调用一个方法,就能实现跨域了,那如何实现呢。看到Controller有个this.Json方法,类型是JsonResult,我们可以参考这个类。定义一个类JsonpResult,派生于JsonResult,在ExecuteResult方法根据callback获取函数名,然后传入json字符串作为函数参数。

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 ArgumentNullException("context");
            }
            if ((JsonRequestBehavior == JsonRequestBehavior.DenyGet) &&
                  String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException();
            }
            var response = context.HttpContext.Response;
            if (!String.IsNullOrEmpty(ContentType))
                response.ContentType = ContentType;
            else
                response.ContentType = CallbackApplicationType;
            if (ContentEncoding != null)
                response.ContentEncoding = this.ContentEncoding;
            if (Data != null)
            {
                String buffer;
                var request = context.HttpContext.Request;
                var serializer = new JavaScriptSerializer();
                if (request[JsonpCallbackName] != null)
                    buffer = String.Format("{0}({1})", request[JsonpCallbackName], serializer.Serialize(Data));//首先根据callback获取获取函数名,然后传入json字符串作为函数参数
                else
                    buffer = serializer.Serialize(Data);
                response.Write(buffer);
            }
        }
    }

  

JsonpResult类有了,但是想在Controller这样使用this.Jsonp,所以为Controller类定义一个扩展方法,

 

public static class ControllerExtension
    {
        public static JsonpResult Jsonp(this Controller controller, object data)
        {
            JsonpResult result = new JsonpResult()
            {
                Data = data,
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            }; 
            return result;
        }
    }

  

这是在Controller就可以直接使用this.Jsonp了,把跨域服务端的代码改下


public ActionResult Index() { return this.Jsonp(new { name = "server JsonpResult" }); }

相比上面那个简洁多了

再次打开http://localhost:62203/home/index

image

同样,站点localhost:62203从站点localhost:16308获取到了数据,和上面的一样

posted on 2016-06-22 09:56  坐在海边看星星  阅读(340)  评论(0编辑  收藏  举报