[转载]由【说说JSON和JSONP..】博文,想到的MVC 扩展 – DotDot – 博客园.
前言
今天看到随它去吧大牛的 【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例 文章,利用JSONP的跨域令人倍感狂喜。于是想,自己动手针对ASP.NET MVC 进行一些扩展,让其更好的支持Jsonp。
关于 JSONP 的详情这里就不介绍了,请看 ——随它去吧: 【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
扩展要点
- 默认约定 Callback 方法名为 Action名,当然也可以提供覆盖。
- 自定义JsonpResult,让其返回Js文件类型的响应,看过MVC 源码的同学都知道,这其实很简单(请打开MVC 源码中的JavaScriptResult.cs文件查看)。
- 为JsonpResult提供Json序列化器。同样参考MVC源码。
- 定义一个JsonpController 继承自Controller 方便使用JsonpResult。
其他:
除了上面3点外,我还定义了一个JsonpViewResult,故名思议就是可以支持View。
核心源码:
JsonpResult public class JsonpResult : ActionResult { public string Json { get ; set ; } public string Callback { get ; set ; } public override void ExecuteResult(ControllerContext context) { if (context == null ) { throw new ArgumentNullException( "context" ); } HttpResponseBase response = context.HttpContext.Response; response.ContentType = "application/x-javascript" ; if (Callback == null ) { Callback = context.RouteData.Values[ "action" ].ToString(); } response.Write( string .Format( "{0}({1})" , Callback, Json)); } } |
JsonpResult 里主要是设置Respone 里的 ContentType 类型, 同时判断Callback是否为null ,如果是则表示使用约定:以Action名为回调函数名。
JsonpViewResult public class JsonpViewResult : ViewResult { public override void ExecuteResult(ControllerContext context) { base .ExecuteResult(context); //if //context.HttpContext.Request.UrlReferrer.Host=="YourDomain" //return ContentType="text/Html" //else: HttpResponseBase response = context.HttpContext.Response; response.ContentType = "application/x-javascript" ; } } |
JsonpViewResult 只需要简单的继承ViewResult ,让后同样设置返回类型即可。
public class JsonpController : Controller { protected internal virtual ActionResult Jsonp( string json) { return new JsonpResult { Json = json }; } protected internal virtual ActionResult Jsonp( string json, string callback) { return new JsonpResult { Json = json, Callback = callback }; } protected internal virtual ActionResult Jsonp( object data) { JavaScriptSerializer serializer = new JavaScriptSerializer(); return new JsonpResult { Json = serializer.Serialize(data) }; } protected internal virtual ActionResult Jsonp( object data, string callback) { JavaScriptSerializer serializer = new JavaScriptSerializer(); return new JsonpResult { Json = serializer.Serialize(data), Callback = callback }; } protected internal virtual ActionResult Jsonp( object data, string callback, JavaScriptTypeResolver resolver) { JavaScriptSerializer serializer = new JavaScriptSerializer(resolver); return new JsonpResult { Json = serializer.Serialize(data), Callback = callback }; } protected internal virtual ActionResult Jsonp( object data, JavaScriptTypeResolver resolver) { JavaScriptSerializer serializer = new JavaScriptSerializer(resolver); return new JsonpResult { Json = serializer.Serialize(data) }; } protected internal ViewResult JsonpView( object model) { return JsonpView( null /* viewName */ , model); } protected internal ViewResult JsonpView( string viewName, object model) { if (model != null ) { ViewData.Model = model; } return new JsonpViewResult { ViewName = viewName, ViewData = ViewData, TempData = TempData }; } } |
JsonpController 对 JsonpResult 和 JsonpViewResult 进行了封装方便使用。
使用示例
新建两个MVCApplictiong 项目,我这里为ASP.NET MVC 3 的项目
在第一个项目为本地域,主要进行Jsonp的跨域请求:
View <h2> Index</h2> <script> var GetCustomers = function (data) { alert(data[0].Address ); }; var script = document.createElement( 'script' ); script.setAttribute( 'src' , url); document.getElementsByTagName( 'head' )[0].appendChild(script); </script> <script> var GetCustomer = function (data) { alert(data.Address+ " " +data.Name+ " " +data.ID); }; var script = document.createElement( 'script' ); script.setAttribute( 'src' , url); document.getElementsByTagName( 'head' )[0].appendChild(script); </script> <script> var GetPI = function (data) { alert(data); }; var script = document.createElement( 'script' ); script.setAttribute( 'src' , url); document.getElementsByTagName( 'head' )[0].appendChild(script); </script> |
第二个项目为远程域,它负责提供请求的数据封装:
Controller public ActionResult GetCustomers() { var customers = new []{ new Customer{ Address= "长安街1" , Id=1, Name= "张三" }, new Customer{ Address= "长安街2" , Id=2, Name= "李四" }, new Customer{ Address= "长安街3" , Id=3, Name= "dudu" }, new Customer{ Address= "长安街4" , Id=4, Name= "DotDot" }, new Customer{ Address= "长安街5" , Id=5, Name= "随它去吧" } }; return Jsonp(customers); } public ActionResult Customer( int id) { var customers = new []{ new Customer{ Address= "长安街1" , Id=1, Name= "张三" }, new Customer{ Address= "长安街2" , Id=2, Name= "李四" }, new Customer{ Address= "长安街3" , Id=3, Name= "dudu" }, new Customer{ Address= "长安街4" , Id=4, Name= "DotDot" }, new Customer{ Address= "长安街5" , Id=5, Name= "随它去吧" } }; var customer = customers.FirstOrDefault(c => c.Id == id); return JsonpView(customer); } public ActionResult Calculate( string callback) { var PI = Math.PI; return Jsonp(PI, callback); } |
Controller中使用了 JsonpViewResult 因此可以对应的建立一个 View视图。在视图里直接指定Callback和Json数据(当然也可以利用绑定)
@model MvcApplication3.Models.Customer
@{
Layout = null;
}
GetCustomer( { Address:’@Model.Address’, ID:’@Model.Id’, Name:’@Model.Name’ } )
一些未考虑的事情
1、callback 方法为JS全局的
2、JsonpViewResult仍旧可以扩展成当为本域请求时显示Html,其他域则返回 Js,(可扩展对外服务)
示例源码:
https://github.com/IndexKey/JsonpExtengForASP.NET-MVC