WCF REST开启Cors 解决 No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 405.
现象:
编写了REST接口:
[ServiceContract] public interface IService1 { [OperationContract] [WebInvoke(UriTemplate = "/TestMethod", Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json )] string TestMethod(CompositeType value); }
数据契约
[DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } }
服务实现
public class Service1 : IService1 { public string TestMethod(CompositeType value) { return string.Format("You entered: {0}", value.StringValue); } }
ajax调用
$(document).ready(function () { $("button").click(function () { alert("clicked"); var data = $("#txt").val(); var postdata = {}; var data_obj = {"BoolValue" : "true" , "StringValue": data} postdata["value"] = data_obj; var url = "https://tmdev01.tm00.com/testwcf/service1.svc/TestMethod"; $.ajax({ type: "POST", url: url, contentType: "application/json; charset=utf-8", data: JSON.stringify(postdata), dataType: "json", success: function(data) {console.log(data);}, error: function(a,b,c) {console.log(a);} }); }); });
报错:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 405.
方法1:
全局请求拦截,处理OPTION谓词
protected void Application_BeginRequest(object sender, EventArgs e) { HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost"); if (HttpContext.Current.Request.HttpMethod == "OPTIONS") { HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE"); HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept"); HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000"); HttpContext.Current.Response.End(); } }
方法2:
定义跨域资源共享相关常量
class CorsConstants { internal const string Origin = "Origin"; internal const string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; internal const string AccessControlRequestMethod = "Access-Control-Request-Method"; internal const string AccessControlRequestHeaders = "Access-Control-Request-Headers"; internal const string AccessControlAllowMethods = "Access-Control-Allow-Methods"; internal const string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; internal const string PreflightSuffix = "_preflight_"; }
创建cors属性
public class CorsEnabledAttribute : Attribute, IOperationBehavior { public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { } public void Validate(OperationDescription operationDescription) { } }
创建自定义消息分发拦截器
class CorsEnabledMessageInspector : IDispatchMessageInspector { private List corsEnabledOperationNames; public CorsEnabledMessageInspector(List corsEnabledOperations) { this.corsEnabledOperationNames = corsEnabledOperations.Select(o => o.Name).ToList(); } public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { HttpRequestMessageProperty httpProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; object operationName; request.Properties.TryGetValue(WebHttpDispatchOperationSelector.HttpOperationNamePropertyName, out operationName); if (httpProp != null && operationName != null && this.corsEnabledOperationNames.Contains((string)operationName)) { string origin = httpProp.Headers[CorsConstants.Origin]; if (origin != null) { return origin; } } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { string origin = correlationState as string; if (origin != null) { HttpResponseMessageProperty httpProp = null; if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name)) { httpProp = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; } else { httpProp = new HttpResponseMessageProperty(); reply.Properties.Add(HttpResponseMessageProperty.Name, httpProp); } httpProp.Headers.Add(CorsConstants.AccessControlAllowOrigin, origin); } } }
class EnableCorsEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { List corsEnabledOperations = endpoint.Contract.Operations .Where(o => o.Behaviors.Find() != null) .ToList(); endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CorsEnabledMessageInspector(corsEnabledOperations)); } public void Validate(ServiceEndpoint endpoint) { } }
class PreflightOperationBehavior : IOperationBehavior { private OperationDescription preflightOperation; private List allowedMethods; public PreflightOperationBehavior(OperationDescription preflightOperation) { this.preflightOperation = preflightOperation; this.allowedMethods = new List(); } public void AddAllowedMethod(string httpMethod) { this.allowedMethods.Add(httpMethod); } public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { dispatchOperation.Invoker = new PreflightOperationInvoker(operationDescription.Messages[1].Action, this.allowedMethods); } public void Validate(OperationDescription operationDescription) { } }
class PreflightOperationInvoker : IOperationInvoker { private string replyAction; List allowedHttpMethods; public PreflightOperationInvoker(string replyAction, List allowedHttpMethods) { this.replyAction = replyAction; this.allowedHttpMethods = allowedHttpMethods; } public object[] AllocateInputs() { return new object[1]; } public object Invoke(object instance, object[] inputs, out object[] outputs) { Message input = (Message)inputs[0]; outputs = null; return HandlePreflight(input); } public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) { throw new NotSupportedException("Only synchronous invocation"); } public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) { throw new NotSupportedException("Only synchronous invocation"); } public bool IsSynchronous { get { return true; } } Message HandlePreflight(Message input) { HttpRequestMessageProperty httpRequest = (HttpRequestMessageProperty)input.Properties[HttpRequestMessageProperty.Name]; string origin = httpRequest.Headers[CorsConstants.Origin]; string requestMethod = httpRequest.Headers[CorsConstants.AccessControlRequestMethod]; string requestHeaders = httpRequest.Headers[CorsConstants.AccessControlRequestHeaders]; 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(CorsConstants.AccessControlAllowOrigin, origin); } if (requestMethod != null && this.allowedHttpMethods.Contains(requestMethod)) { httpResponse.Headers.Add(CorsConstants.AccessControlAllowMethods, string.Join(",", this.allowedHttpMethods)); } if (requestHeaders != null) { httpResponse.Headers.Add(CorsConstants.AccessControlAllowHeaders, requestHeaders); } return reply; } }
给接口增加 cors属性标记
[ServiceContract] public interface IService1 { [OperationContract] [WebInvoke(UriTemplate = "/TestMethod", Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json ),CorsEnabled] string TestMethod(CompositeType value); }
CORS详细说明:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS