自托管WCF服务配置CORS后仍报预检请求无Access-Control-Allow-Origin头如何解决
问题根因
你遇到的CORS不生效问题由两个核心错误导致:
- 浏览器发起跨域请求前会先发送
OPTIONS预检请求,WCF默认不会将该类请求路由到你的服务接口,你当前实现的消息拦截器无法触发生效,导致响应没有携带CORS头 - 你配置的响应头字段名错误,
Access-Control-Request-Method是请求头字段,响应头需要返回Access-Control-Allow-Methods
解决步骤
1. 修正App.config配置
你当前仅配置了basicHttpBinding,但服务实际使用webHttpBinding,需要补充该绑定的跨域配置,同时检查行为扩展配置正确性:
<system.serviceModel> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> <bindings> <!--basicHttpBinding--> <basicHttpBinding> <binding name="BasicBinding" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"> <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647" /> </binding> </basicHttpBinding> <!-- 新增webHttpBinding配置,开启跨域脚本访问 --> <webHttpBinding> <binding name="corsBinding" crossDomainScriptAccessEnabled="true"> <security mode="None" /> </binding> </webHttpBinding> </bindings> <services> <!-- endpoint新增bindingConfiguration指定corsBinding --> <service name="##.ServiceScanClientService" behaviorConfiguration="serviceBehavior"> <endpoint address="" binding="webHttpBinding" bindingConfiguration="corsBinding" behaviorConfiguration="jsonBehavior" contract="##.IServiceScanClientService" /> </service> </services> <extensions> <behaviorExtensions> <!-- 注意:此处##替换为实际命名空间,程序集名不要带多余空格 --> <add name="crossOriginResourceSharingBehavior" type="##.BehaviorAttribute, ServiceScan" /> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior name="serviceBehavior"> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="jsonBehavior"> <crossOriginResourceSharingBehavior /> <webHttp /> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel>
2. 新增OPTIONS预检请求处理逻辑
修改CustomHeaderMessageInspector的AfterReceiveRequest方法,直接处理预检请求,终止后续流程直接返回响应:
using System.Net; using System.ServiceModel.Web; public class CustomHeaderMessageInspector : IDispatchMessageInspector { Dictionary<string, string> requiredHeaders; public CustomHeaderMessageInspector(Dictionary<string, string> headers) { requiredHeaders = headers ?? new Dictionary<string, string>(); } public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { // 处理OPTIONS预检请求 if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS") { var response = WebOperationContext.Current.OutgoingResponse; response.StatusCode = HttpStatusCode.OK; foreach (var item in requiredHeaders) { response.Headers.Add(item.Key, item.Value); } // 构造空响应,终止后续流程 var reply = Message.CreateMessage(MessageVersion.None, ""); var httpResponse = new HttpResponseMessageProperty { StatusCode = HttpStatusCode.OK }; reply.Properties[HttpResponseMessageProperty.Name] = httpResponse; reply.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw)); throw new FaultException("Preflight handled"); } string displayText = $"Server has received the following message:{Environment.NewLine}{request}{Environment.NewLine}"; Console.WriteLine(displayText); return null; } public void BeforeSendReply(ref Message reply, object correlationState) { if (reply.Properties.TryGetValue(HttpResponseMessageProperty.Name, out var httpResponseObj) && httpResponseObj is HttpResponseMessageProperty httpHeader) { foreach (var item in requiredHeaders) { // 避免重复添加头 if (!httpHeader.Headers.AllKeys.Contains(item.Key)) { httpHeader.Headers.Add(item.Key, item.Value); } } } } }
3. 修正响应头字段名
修改BehaviorAttribute中的错误字段名,补充可选配置:
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) { var requiredHeaders = new Dictionary<string, string> { { "Access-Control-Allow-Origin", "*" }, // 修正字段名,将Access-Control-Request-Method改为Access-Control-Allow-Methods { "Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS" }, { "Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With" }, // 可选:缓存预检请求结果24小时,减少重复请求 { "Access-Control-Max-Age", "86400" } }; endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders)); }
4. 可选客户端适配
如果POST请求携带自定义头,需要在Access-Control-Allow-Headers中补充对应的头字段名,确保预检请求校验通过。
内容的提问来源于stack exchange,提问作者gmd0123
相关产品推荐
相关产品推荐

