• WCF支持WEB跨域访问


    方法一


    WCF Service

    	[ServiceBehavior(
    		InstanceContextMode = InstanceContextMode.PerCall
    		, ConcurrencyMode = ConcurrencyMode.Multiple
    		, UseSynchronizationContext = false)]//禁用UI线程
    	class CrossService : ICrossService
    	{
    		//http://localhost:13333/CrossService/CrossCall?pJson=abcd1234
    		public System.IO.Stream GetCrossCall(string pJson)
    		{
    			return PostCrossCall(pJson);
    		}
    
    		//http://localhost:13333/CrossService/CrossCall
    		public System.IO.Stream PostCrossCall(string pJson)
    		{
    			string _origin = WebOperationContext.Current.IncomingRequest.Headers["Origin"];
    			if (_origin != null)
    			{
    				WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", _origin);
    			}
    
    
    			return new System.IO.MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(string.Format("{0}", pJson)));
    		}
    		public Message OptionsCrossCall()
    		{
    			return CreateOptionsMessage();
    		}
    
    		/// <summary>
    		/// 创建OPTIONS报文
    		/// </summary>
    		/// <returns></returns>
    		Message CreateOptionsMessage()
    		{
    			string _origin = WebOperationContext.Current.IncomingRequest.Headers["Origin"];
    			string _replyAction = "OPTIONS";
    			Message _reply = Message.CreateMessage(MessageVersion.None, _replyAction);
    			HttpResponseMessageProperty _httpResponse = new HttpResponseMessageProperty();
    			_reply.Properties.Add(HttpResponseMessageProperty.Name, _httpResponse);
    
    			_httpResponse.SuppressEntityBody = true;
    			_httpResponse.StatusCode = HttpStatusCode.OK;
    			if (_origin != null)
    			{
    				_httpResponse.Headers.Add("Access-Control-Allow-Origin", _origin);
    			}
    
    			_httpResponse.Headers.Add(
    				"Access-Control-Allow-Methods", "POST, PUT, DELETE");//client post header "Access-Control-Request-Method"
    
    			_httpResponse.Headers.Add(
    				"Access-Control-Allow-Headers", "Content-Type, Accept");//client post header "Access-Control-Request-Headers"
    
    			return _reply;
    		}
    
    
    		/***********客户端js调用方式***********
    		  
    		var xhttp = new XMLHttpRequest();
    		xhttp.onload=function(){console.log(xhttp.responseText);};
    		xhttp.open("POST", "http://localhost:13333/CrossService/CrossCall");
    		xhttp.setRequestHeader('Content-Type', 'application/json');//没有设置application/json的话,会报异常消息
    		xhttp.send(12345);//字符要使用转义字符串:"" {pJson:\"adfb\"} ""
    		 */
    
    
    
    		/* 可以把参数设置为System.IO.Stream类型,WEB客户端Content-Type设置为multipart/form-data
    		*这样可以直接按数据流上传
    
    		class CrossService : ICrossService
    		{
    		public void DoWork(System.IO.Stream input)
    		{
    		System.IO.StreamReader sr = new StreamReader(input);
    		string s = sr.ReadToEnd();
    		sr.Dispose();
    		// Do work here
    		}
    		}
    
    		*/
    
    	}
    
    
    
    	[ServiceContract]
    	public interface ICrossService
    	{
    		//[OperationContract]
    		[WebGet(UriTemplate = "/CrossCall?pJson={pJson}")]
    		System.IO.Stream GetCrossCall(string pJson);
    
    
    		[WebInvoke(UriTemplate = "/CrossCall",
    			Method = "POST")]
    		System.IO.Stream PostCrossCall(string pJson);
    
    		/// <summary>
    		/// 非GET操作跨域要增加OPTIONS请求处理
    		/// </summary>
    		/// <param name="pJson"></param>
    		/// <returns></returns>
    		[WebInvoke(UriTemplate = "/CrossCall",//路径和POST(非GET)路径相同
    			Method = "OPTIONS")]
    		Message OptionsCrossCall();
    
    	}
    

      

    config增加webHttpBinding和webHttp behavior

        <services>
          <service name="Test.CrossService" 
    ..................................>
            <endpoint address="http://localhost:13333/CrossService"
                      binding="webHttpBinding"
                      behaviorConfiguration="HttpWcfBehavior"
                      contract="Test.CrossService"/>
    ...................................
    
          </service>
        </services>
    
        <behaviors>
    
          <endpointBehaviors>
            <behavior name="HttpWcfBehavior">
    
            <!--webHttpBehavior:启用 Windows Communication Foundation (WCF) 服务的 Web 编程模型。
            WebHttpBehavior 行为与 WebHttpBinding 绑定一起使用时,支持 WCF 公开和访问 Web 样式服务。-->
              <webHttp automaticFormatSelectionEnabled="true" />
            
            </behavior>
          </endpointBehaviors>
    .............
        </behaviors>
    

    方法二 


    实现一个endpotin behavior通过endpoint拦截消息,处理OPTIONS请求

    例子:

    public class MessageInspector : IDispatchMessageInspector
    {
    	string BindingName;
    	public MessageInspector(string bindingName)
    	{
    		BindingName = bindingName;
    	}
    
    	/// <summary>
    	/// invoke操作之前执行
    	/// </summary>
    	/// <param name="request"></param>
    	/// <param name="channel"></param>
    	/// <param name="instanceContext"></param>
    	/// <returns></returns>
    	public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    	{
    		HttpRequestMessageProperty _httpProps = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
    		if (null != _httpProps && "OPTIONS".Equals(_httpProps.Method))
    		{
    			return "TRUE";
    		}
    
    		return null;
    	}
    
    	/// <summary>
    	/// 发送reply给客户端之前执行
    	/// </summary>
    	/// <param name="reply"></param>
    	/// <param name="correlationState"></param>
    
    	public void BeforeSendReply(ref Message reply, object correlationState)
    	{
    		if ("TRUE".Equals(correlationState))
    		{
    			reply = T8Service.CreateOptionsMessage();
    		}
    	}
    
    
    }
    
    
    
    public class MessageInspectorBehavior : IEndpointBehavior
    {
    	string BindingName;
    	public MessageInspectorBehavior(string bindingName)
    	{
    		BindingName = bindingName;
    	}
    	public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    	{
    	}
    
    	public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    	{
    	}
    
    	public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    	{
    			
    		endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageInspector(BindingName));
    	}
    
    	public void Validate(ServiceEndpoint endpoint)
    	{
    	}
    }
    

      

    在启动ServiceHost时加入Behavior

    private static ServiceHost CrossServiceHost
    {
    	get
    	{
    		if (_CrossServiceHost == null || _CrossServiceHost.State == CommunicationState.Closed)
    		{
    			_CrossServiceHost = null;
    			_CrossServiceHost = new ServiceHost(typeof(CrossService));
    
    			var endpoints = _CrossServiceHost.Description.Endpoints;
    
    			foreach (var _endpoint in endpoints)
    			{
    				if (_endpoint.Binding is WebHttpBinding) ;
    				{
    					_endpoint.Behaviors.Add(new MessageInspectorBehavior(_endpoint.Binding.Name));
    				}
    			}
    		}
    		return _CrossServiceHost;
    	}
    }
    private static ServiceHost _CrossServiceHost = null;
    

      

    备注


    在.net Window Program程序中使用HttpWebRequest 模拟HTTP请求调用WCF可以进入basicHttpBinding和webHttpBinding,

    调用basicHttpBinding时

    报文Content-Type 需设置为 "text/xml; charset=utf-8",

    同时需增加一个SOAPAction报头声明调用服务端方法的命名url,传递报文数据为SOAP 格式的XML

     

    由于 BasicHttpBinding接收不了 OPTIONS 报文(空报文直接报错,进不了BasicHttpBinding EndPoint),所以BasicHttpBinding不能跨域。

    C# 使用HttpWebRequest通过BasicHttpBinding调用 WCF 方法例子:

    public static string CallWCFBasicHttpBinding(
    	string baseAddress, //BasicHttpBinding address
    	string soapAction,  //调用方法的SOAP 声明
    	string metodName, //方法名称(生成XML报文用)
    	string paramName, //方法参数名称(生成XML报文用,格式中使用一个参数)
    	string paramValue //参数值
    	)
    {
    
    	string _url = baseAddress;
    	string _sendMsg = "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">" +
    	"<s:Body><{0} xmlns="http://tempuri.org/"><{1}>{2}</{1}></{0}></s:Body>" +
    	"</s:Envelope>";
    	_sendXml =  string.Format(_sendXml, metodName, paramName, paramValue);
    
    	HttpWebRequest _webRequest = System.Net.WebRequest.Create(_url) as HttpWebRequest;
    	_webRequest.Method = "POST";
    	_webRequest.ContentLength = _sendXml.Length;
    	_webRequest.ContentType = "text/xml; charset=utf-8";//这里必需是text/xml
    
    	_webRequest.Headers["SOAPAction"] = soapAction;// "http://tempuri.org/IService1/方法名称";
    
    	Stream _smWebReq = null;
    	try
    	{
    		_smWebReq = _webRequest.GetRequestStream();
    	}
    	catch (System.Exception er)
    	{
    		return er.Message;
    	}
    	StreamWriter _requestWriter = null;
    	try
    	{
    		_requestWriter = new StreamWriter(_smWebReq);
    		_requestWriter.Write(_sendXml);
    	}
    	catch (System.Exception er)
    	{
    		return er.Message;
    	}
    	finally
    	{
    		if (null != _requestWriter)
    		{
    			_requestWriter.Close();
    			_requestWriter = null;
    
    		}
    		if (null != _smWebReq)
    		{
    			_smWebReq.Close();
    			_smWebReq = null;
    
    		}
    	}
    
    	StreamReader _respReader = null;
    	Stream _smWebResp = null;
    	string _retString = "";
    	try
    	{
    		_smWebResp = _webRequest.GetResponse().GetResponseStream();
    		_respReader = new StreamReader(_smWebResp);
    		_retString = _respReader.ReadToEnd();
    	}
    	catch (System.Exception er)
    	{
    		return er.Message;
    	}
    	finally
    	{
    		if (null != _respReader)
    		{
    			_respReader.Close();
    			_respReader = null;
    		}
    		if (null != _smWebResp)
    		{
    			_smWebResp.Close();
    			_webRequest = null;
    		}
    	}
    
    	return _retString;//返回XML格式信息
    }
    

      

     
    如果C# 使用HttpWebRequest通过WebHttpBinding调用 WCF 方法,与浏览器调用相同,通过方法路径调用,

    报文Content-Type 需设置为 "application/json",不需要SOAPAction报头

     

    参考

    https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

  • 相关阅读:
    Jni如何传递并且修改两个基础参数
    【转】对于JNI方法名,数据类型和方法签名的一些认识
    Android应用程序开机开机启动
    Android程序中Acticity间传递数据
    2014-7-6 学期总结
    程序员的美:极致与疯狂
    《重构:改善既有代码的设计》——关于代码注释的唠叨
    2014-5-5 近期小结和计划
    Android:RelativeLayout 内容居中
    图像处理:图像灰度化
  • 原文地址:https://www.cnblogs.com/Grart/p/6903141.html
Copyright © 2020-2023  润新知