zoukankan      html  css  js  c++  java
  • 使WCF支持JSONP调用

    这两天在研究如何通过JSONP去实现跨域调用WCF服务,找到了以下几种解决方式:

    解决方案一: 

    在服务器端直接通过调用DataContractJsonSerializer或JavaScriptSerializer对数据进行JSON序列化后返回Stream或直接调用Response.Write方法把序列化后的数据返回给业务系统: 

    以下的JsonHelper类实现对于JSON调用和JSONP调用进行分别返回:若是JSONP调用则把JSON数据再根据传入的jsoncallback参数封装。

        public static class JSonHelper

        {

            /// <summary>
            
    /// 输出JSON,支持JSON和JSONP
            
    /// </summary>
            
    /// <param name="obj"></param>
            public static void OutputJSON(this object obj)
            {
                
    string result;
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                result = serializer.Serialize(obj);
                
    string pass=System.Web.HttpContext.Current.Request.QueryString["jsoncallback"];
                
    if (string.IsNullOrEmpty(pass) == false)
                {
                    System.Web.HttpContext.Current.Response.Write(
                        
    string.Format("{0}({1})", pass, result));
                }
                
    else
                {
                    System.Web.HttpContext.Current.Response.Write(result);
                }
            }


            
    public static T ParseJSON<T>(this string input)
            {
                T result;
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                result = serializer.Deserialize<T>(input);
                
    return result;
            }
        }

      

      所有服务则定为返回值为void, 代码如下:     

        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 

        [ServiceContract(Namespace ="")]

        public class JSonpService
        {
            [OperationContract]
         [WebGet(ResponseFormat = WebMessageFormat.Xml)]  // 注意这里一定要用Xml而不是Json,否则返回的数据会被多封装一个Null值进去。

            publicvoid GetPurchaseGroup()
            {
                JsonMsg msg =new JsonMsg(){Name ="XX", Value ="YY"};            
                m_JsonMsg.OutputJSON();
            }
        }

    当然也可以把JSonHelper类实现为返回Stream流,每个服务器端方法都返回Stream流。从通用性来说,返回Stream的方式兼容性更强,并且可以自己控制返回数据的Content-Type.

    虽然通过解决方案一可以实现WCF支持JSONP方式调用,但是这种解决方案侵入性太强,改变了服务的接口(返回值),因此并不是特别好的解决方案,所以我们继续往下看:

    解决方案二:
    .NET 4.0的新功能.NET 4.0已经增添了原生支持JSONP跨域调用WCF. 主要涉及两点:crossDomainScriptAccessEnabled 配置项和JavascriptCallbackBehaviorAttribute类下面给出 crossDomainScriptAccessEnabled的使用例子:

    代码
    <system.serviceModel>
        
    <behaviors>
          
    <endpointBehaviors>
            
    <behavior name="CorsairStudio.Core.WebSite.CrossDomainServiceAspNetAjaxBehavior">
              
    <enableWebScript />
            
    </behavior>
          
    </endpointBehaviors>
        
    </behaviors>
        
    <bindings>
          
    <webHttpBinding>
            
    <binding name="webHttpBindingJsonP" crossDomainScriptAccessEnabled="true"/>
          
    </webHttpBinding>
        
    </bindings>
        
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
          multipleSiteBindingsEnabled="true" />
        
    <services>
          
    <service name="CorsairStudio.Core.WebSite.CrossDomainService">
            
    <endpoint address="" behaviorConfiguration="CorsairStudio.Core.WebSite.CrossDomainServiceAspNetAjaxBehavior"
              binding="webHttpBinding" bindingConfiguration="webHttpBindingJsonP" 
                      contract="CorsairStudio.Core.WebSite.CrossDomainService" />
          
    </service>
        
    </services>
      
    </system.serviceModel>

      通过启用 ,WCF便已经可以支持JSONP跨域调用。而属性JavascriptCallbackBehaviorAttribute则用于定义客户端JS调用时使用的URL中的参数名字:

    代码
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        [JavascriptCallbackBehavior(UrlParameterName="callback")]
        public class JSonpService

        ... 

      PS: 在.NET4中通过JSON调用WCF会被多封装一个'd', 但如果通过JSONP调用,则不会多封装这个'd'.

    参见:http://bendewey.wordpress.com/2009/11/24/using-jsonp-with-wcf-and-jquery/

    .NET 4已经给出了完整的解决方案,但目前的多数项目都还是基于.NET 3.5的,因此解决方案二依然是雾中花,需要找更实际的解决方案。

    解决方案三:HttpModule
    HttpModule可以拦截用户的请求或修改输出返回的内容,利用这个特性,可以尝试扩展一个HttpModule为JSONP调用时修改返回的数据

    JSONPModule
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Web;

    namespace NIntegrate.Web
    {
        /// <summary>
        /// 用于.NET 3.5的JSONP技术支持HttpModule.(.NET 4.0使用crossDomainScriptAccessEnabled=true配置项进行支持)
        /// </summary>
        public class JSonpilizeModule : IHttpModule
        {
            private const string RESPONSE_CONTENT_TYPE = "application/json";
            internal const string REQUEST_QUERY_KEY = "callback";

            #region IHttpModule Members
            public void Dispose()
            {
            }

            public void Init(HttpApplication app)
            {
                app.ReleaseRequestState += new EventHandler(app_ReleaseRequestState);
            }

            void app_ReleaseRequestState(object sender, EventArgs e)
            {
                HttpApplication app = (HttpApplication)sender;
                HttpResponse response = app.Response;
                HttpRequest requset = app.Request;

                // 若返回对象是Json对象,则进行处理
                if (response.ContentType.ToLowerInvariant().Contains(RESPONSE_CONTENT_TYPE))
                {
                    response.Filter = new JSonpilizingResponseFilter(response, requset);
                }
            }

            #endregion
        }

       
        public class JSonpilizingResponseFilter : Stream
        {
            private Stream _Sink;
            private long _Position;
            private string _Key;
            private HttpResponse _Response;
            private HttpRequest _Requset; 

            public override bool CanRead { get { return false; } }

            public override bool CanSeek { get { return false; } }

            public override bool CanWrite { get { return true; } }

            public override void Flush() { this._Sink.Flush(); }

            public override long Length { get { return 0; } }

            public override long Position { get { return _Position; } set { _Position = value; } }

            public JSonpilizingResponseFilter(HttpResponse response, HttpRequest request)
            {
                this._Requset = request;
                this._Response = response;

                this._Sink = response.Filter;
                this._Key = request[JSonpilizeModule.REQUEST_QUERY_KEY];
            }

            public override int Read(byte[] buffer, int offset, int count)
            {
                return _Sink.Read(buffer, offset, count);
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                return 0;
            }

            public override void Close()
            {
                this._Sink.Close();
            }

            public override void SetLength(long value)
            {
                this._Sink.SetLength(value);
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                byte[] pageBuffer;

                string oringalValue = Encoding.Default.GetString(buffer);
                //首先判断有没有系统错误
                if (HttpContext.Current.Error == null)
                {
                    _Response.ClearHeaders();

                    // 若Request["callback"]没有值则是JSonp调用(为保持和4.0实现一致,当非Jsonp调用时,会封装一个d:)
                    if (string.IsNullOrEmpty(this._Key) == true)
                    {
                        // 返回类型为application/json
                        _Response.ContentType = "application/json; charset=utf-8";

                        oringalValue = string.Format("{0}({1})"this._Key, oringalValue);
                    }
                    // 否则是Jsonp调用(保持和4.0实现一致,不封装d:)
                    else
                    {
                        // 返回类型为application/x-javascript
                        _Response.ContentType = "application/x-javascript; charset=utf-8";

                        Regex regex = new Regex(@"{""d"":(?<OringalValue>{.+})}");
                        Match match = regex.Match(oringalValue);

                        if (match.Success)
                        {
                            oringalValue = string.Format("{0}({1})"this._Key, match.Groups["OringalValue"].Value);
                        }                    
                    }

                    pageBuffer = Encoding.Default.GetBytes(oringalValue);
                    _Sink.Write(pageBuffer, 0, pageBuffer.Length);
                    _Response.AppendHeader("Content-Length", pageBuffer.Length.ToString());
                }
            }
        } }

     然后修改Web.config文件

    Web.Config
    <system.web>
       ...
       
    <httpModules>
         ...
         
    <add name="JSONPModule" type="NIntegrate.Web.JSONPModule, NIntegrate, Version=X.X.X.X, Culture=neutral, PublicKeyToken=e2b9e2165dbdd5e6"/>
       
    </httpModules>
     
    </system.web>
     ...
     
    <system.webServer>
       ...
       
    <modules>
         ...
         
    <add name="JSONPModule" preCondition="managedHandler" type="NIntegrate.Web.JSONPModule, NIntegrate, Version=X.X.X.X, Culture=neutral, PublicKeyToken=e2b9e2165dbdd5e6"/>
       
    </modules>
       ...
     
    </system.webServer>

     
    参见:http://blog.csdn.net/teddyma/archive/2010/03/06/5353076.aspx
    这是在.NET 3.5SP1下通过的办法,不过.NET4已经提供了内置的实现了。

    解决方案四:MSDN上的例子中的JSONPBehaviorAtrribute

    该方案利用的是MSDN的一个例子中做的扩展
    参见:http://www.developmentalmadness.com/2009/07/building-single-sign-on-provider-using_06.html
  • 相关阅读:
    MySQL Delete 后,如何快速释放磁盘空间
    浅谈MySQl 主从复制
    MySQL 5.6,5.7 基于Shell 的一键安装
    【MySQL 5.7 】从库1032 报错处理
    633. Sum of Square Numbers
    find a balance point in an array
    Longest Common Prefix
    cubic root
    41. First Missing Positive
    build tree with balanced parenthesis
  • 原文地址:https://www.cnblogs.com/Xrinehart/p/1741588.html
Copyright © 2011-2022 走看看