方法一
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.οnlοad=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