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

原文地址:https://www.cnblogs.com/Grart/p/6903141.html