【问题标题】:Access to WCF WebService from JavaScript - Response to preflight request doesn't pass access control check从 JavaScript 访问 WCF WebService - 对预检请求的响应未通过访问控制检查
【发布时间】:2018-01-07 23:33:46
【问题描述】:

我正在使用 WCF (.svc) WebService,它运行良好 - 我可以毫无问题地从 Postman、PHP 等调用它的方法。但是,当我尝试从 JavaScript/jQuery调用它时> 使用 AJAX,有一个明显的问题——我是从 WS 以外的其他域做的,所以它不会让我做。

这都是关于 POST 方法的。但是,即使我的页面首先发送一个 OPTIONS 方法,也会出现问题:

选项'WS ADDRESS' 405(不允许的方法)

XMLHttpRequest 无法加载 'WS ADDRESS' 对预检请求的响应没有 通过访问控制检查:没有“Access-Control-Allow-Origin”标头 出现在请求的资源上。因此,来源 'MY ADDRESS' 是不允许的 使用权。响应的 HTTP 状态代码为 405。

有详细回复:

好的,我阅读了有关跨域的信息,并且 WS 的 Web.config 包含所有必要的内容(?)

请让我知道我做错了什么,我无法从 JavaScript 访问我的 WS,即使在我看来它配置得很好。但是,它似乎没有发送这些特殊的标头作为响应......提前致谢。

【问题讨论】:

  • 该 405 代码意味着您的服务器已配置为禁止 OPTIONS 请求。这就是 Method Not Allowed 消息的含义。您必须将其配置为使用 200 或 204 成功消息和正确的 Access-Control-Allow-* 标头来响应 OPTIONS 请求。至于如何做到这一点,听起来您需要阅读更多文档并进一步研究它——从stackoverflow.com/questions/16024347/…stackoverflow.com/questions/43911702/…开始

标签: javascript jquery wcf cross-domain svc


【解决方案1】:

XML HttpRequest 允许将数据从 Web 服务器传输到浏览器。然而,浏览器默认阻止CORS。 GET 请求不会更改服务器端的任何内容(没有创建/更新/删除任何内容) - 它只会返回一些数据。

但是,POST/PUT/DELETE 请求将处理请求并在服务器端更改某些内容并向浏览器发出响应。如果响应没有正确的Access-Control-Allow-Origin 标头,浏览器将阻止响应。但这并不重要,因为在发出响应时,服务器已经处理了请求并进行了更改,可能对数据库进行了更改。

为了防止服务器端处理POST/PUT/DELETE请求,浏览器会发送preflight request

prefligth 请求是一个带有 OPTIONS 方法的 http 请求。因此,在发送 POST 请求之前,浏览器会发送一个 OPTIONS 请求,其中包含一个名为 Access-Control-Request-Method 的附加标头,其值为 POST。

405 (Method Not Allowed) 错误表示服务器未配置为接受 OPTIONS 请求。

您可以通过为您的网络调用方法使用通配符来解决此问题,类似于:

[OperationContract]
[WebInvoke(Method = "*", UriTemplate = "/Path", ResponseFormat = WebMessageFormat.Json)]

或者通过向您的[ServiceContract] 添加一个额外的[OperationContract] 来处理类似于这样的OPTIONS 请求:

 [OperationContract(Name = "OptionsMyFunction")]
 [WebInvoke(Method = "OPTIONS", UriTemplate = "/Path", ResponseFormat = WebMessageFormat.Json)]

【讨论】:

    【解决方案2】:

    您可以为此工作实现 .NET IDispatchMessageInspector。

    • 创建一个实现IDispatchMessageInspector的类
    • 创建一个实现Attribute,IEndpointBehavior,IOperationBehavior的类

    只允许在你的类中实现IDispatchMessageInspector的OPTIONS

    代码如下所示

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Description;
    using System.Collections.Generic;
    using System.Net;
    
    namespace WapstyPrintService
    {
        public class MessageInspector : IDispatchMessageInspector
        {
            private ServiceEndpoint _serviceEndpoint;
            Dictionary<string, string> requiredHeaders;
    
            public MessageInspector(ServiceEndpoint serviceEndpoint)
            {
                _serviceEndpoint = serviceEndpoint;
                requiredHeaders = new Dictionary<string, string>();
    
                requiredHeaders.Add("Access-Control-Allow-Origin", "*");
                requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
                requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
            }
    
            /// <summary>
            /// Called when an inbound message been received
            /// </summary>
            /// <param name="request">The request message.</param>
            /// <param name="channel">The incoming channel.</param>
            /// <param name="instanceContext">The current service instance.</param>
            /// <returns>
            /// The object used to correlate stateMsg. 
            /// This object is passed back in the method.
            /// </returns>
            public object AfterReceiveRequest(ref Message request,
                                                  IClientChannel channel,
                                                  InstanceContext instanceContext)
            {
                var httpRequest = (HttpRequestMessageProperty)request
                .Properties[HttpRequestMessageProperty.Name];
                return new
                {
                    origin = httpRequest.Headers["Origin"],
                    handlePreflight = httpRequest.Method.Equals("OPTIONS",
                    StringComparison.InvariantCultureIgnoreCase)
                };
            }
    
            /// <summary>
            /// Called after the operation has returned but before the reply message
            /// is sent.
            /// </summary>
            /// <param name="reply">The reply message. This value is null if the 
            /// operation is one way.</param>
            /// <param name="correlationState">The correlation object returned from
            ///  the method.</param>
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                var state = (dynamic)correlationState;
                if (state.handlePreflight)
                {
                    reply = Message.CreateMessage(MessageVersion.None, "PreflightReturn");
    
                    var httpResponse = new HttpResponseMessageProperty();
                    reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
    
                    httpResponse.SuppressEntityBody = true;
                    httpResponse.StatusCode = HttpStatusCode.OK;
                }
    
                var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
                foreach (var item in requiredHeaders)
                {
                    httpHeader.Headers.Add(item.Key, item.Value);
                }
            }
        }
    }
    

    using System;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    
    namespace WapstyPrintService
    {
        public class BehaviorAttribute : Attribute, IEndpointBehavior,
                                       IOperationBehavior
        {
            public void Validate(ServiceEndpoint endpoint) { }
    
            public void AddBindingParameters(ServiceEndpoint endpoint,
                                     BindingParameterCollection bindingParameters)
            { }
    
            /// <summary>
            /// This service modify or extend the service across an endpoint.
            /// </summary>
            /// <param name="endpoint">The endpoint that exposes the contract.</param>
            /// <param name="endpointDispatcher">The endpoint dispatcher to be
            /// modified or extended.</param>
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
                                              EndpointDispatcher endpointDispatcher)
            {
                // add inspector which detects cross origin requests
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
                                                       new MessageInspector(endpoint));
            }
    
            public void ApplyClientBehavior(ServiceEndpoint endpoint,
                                            ClientRuntime clientRuntime)
            { }
    
            public void Validate(OperationDescription operationDescription) { }
    
            public void ApplyDispatchBehavior(OperationDescription operationDescription,
                                              DispatchOperation dispatchOperation)
            { }
    
            public void ApplyClientBehavior(OperationDescription operationDescription,
                                            ClientOperation clientOperation)
            { }
    
            public void AddBindingParameters(OperationDescription operationDescription,
                                      BindingParameterCollection bindingParameters)
            { }
    
        }
    }
    

    然后将您的消息检查器添加到服务端点行为

    ServiceHost host = new ServiceHost(typeof(myService), _baseAddress);
    foreach (ServiceEndpoint EP in host.Description.Endpoints)
                EP.Behaviors.Add(new BehaviorAttribute());
    

    【讨论】:

      猜你喜欢
      • 2021-03-08
      • 1970-01-01
      • 2016-06-05
      • 2017-04-28
      相关资源
      最近更新 更多