如何为WCF服务添加跨域支持

本文关键字:支持 添加 服务 WCF | 更新日期: 2023-09-27 17:49:32

我试图允许从托管在localhost:80的javascript应用程序到托管在不同端口的WCF REStful服务的POST请求,但不知何故它不起作用。我试过添加自定义属性到头部,以及在我的服务的JSONData方法中以编程方式添加它,但我仍然在我的响应中得到"405方法不允许"。正确的做法是什么?

这是我的界面:

namespace RestService
{
    public class RestServiceImpl : IRestServiceImpl
    {
        #region IRestServiceImpl Members
        public string JSONData()
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            return "Your POST request";
        }
        #endregion
    }
}

和服务代码:

using System.ServiceModel;
using System.ServiceModel.Web;
using System.Web.Script.Services;
namespace RestService
{
    [ServiceContract]
    public interface IRestServiceImpl
    {
        [OperationContract]
        [ScriptMethod]
        [WebInvoke(Method = "POST",
            ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            UriTemplate = "export")]
        string JSONData();
    }
}

最后是配置:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="RestService.RestServiceImpl" behaviorConfiguration="ServiceBehaviour">
        <endpoint address ="" binding="webHttpBinding" contract="RestService.IRestServiceImpl" behaviorConfiguration="web">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <httpProtocol>
      <customHeaders>
         <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
</httpProtocol>  
  </system.webServer>
</configuration>

如何为WCF服务添加跨域支持

这对我来说比Web更好。配置版本:

创建Global.asax

将此方法添加到Global.asax.cs:

using System.Web;
namespace StackOverflow
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
                HttpContext.Current.Response.End();
            }
        }
    }
}

裁判:http://www.dotnet-tricks.com/Tutorial/wcf/X8QN260412-Calling-Cross-Domain-WCF-Service-using-Jquery.html

将这些节点添加到Web.config:

<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*"/>
        <add name="Access-Control-Allow-Headers" value="Content-Type, Accept" />
        <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS" />
        <add name="Access-Control-Max-Age" value="1728000" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

裁判:http://theagilecoder.wordpress.com/2014/07/07/wcf-and-cors-no-access-control-allow-origin-header-is-present-on-the-requested-resource/

为非get请求启用CORS需要的不仅仅是设置Access-Control-Allow-Origin标头-它还需要处理preflight请求,这是OPTIONS请求,它询问服务器是否安全执行可能改变数据的操作(例如,POST, PUT, DELETE)在实际请求发送之前。

我写了一篇关于为WCF添加CORS支持的博文。这不是最简单的实现,但希望在帖子中的代码可以简单地复制/粘贴到您的项目中。

下面的。net代码(global.asax)有一个重要的区别,即代替*,它可以更好地回显原始域,因为这允许通过CORS(例如NTLM/Kerberos)以及Preflight进行身份验证。

void Application_BeginRequest(object sender, EventArgs e)
{
    if (Request.HttpMethod == "OPTIONS")
    {
        Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
        Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        Response.AddHeader("Access-Control-Max-Age", "1728000");
        Response.End();
    }
    else
    {
        Response.AddHeader("Access-Control-Allow-Credentials", "true");
        if (Request.Headers["Origin"] != null)
            Response.AddHeader("Access-Control-Allow-Origin" , Request.Headers["Origin"]);
        else
            Response.AddHeader("Access-Control-Allow-Origin" , "*");
    }
}

上述所有解决方案都修改了CORS响应,以允许所有站点通过使用*属性启用CORS。这对我来说似乎是一个安全风险,因为我想控制哪些站点可以访问我的REST服务。我希望这可以帮助到其他有同样问题的人。

我从修改网页开始。通过使用SpecializedString集合来包含我允许的起源站点,该集合允许将字符串数组存储在MySettings中。代码是在VB。如果需要的话,Net应该很容易移植到c#。

<applicationSettings>
    <YourService.My.MySettings>
        <setting name="AllowedOrigins" serializeAs="Xml">
            <value>
                <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                    <string>http://localhost:62087</string>
                    <string>https://yourallowedorign2:3344</string>
                </ArrayOfString>
            </value>
        </setting>
    </YourService.My.MySettings>
</applicationSettings>

在Global.asax.vb文件中,我对Application_BeginRequest代码做了以下调整。

请注意获取Allowed Origins的支持函数

Private allowedOrigins As String()
Public Function GetAllowedOrigins() As String()
    Dim mySetting As StringCollection = My.Settings.AllowedOrigins
    If mySetting IsNot Nothing Then
        ' If you're using .NET 3.5 or greater:
        Return mySetting.Cast(Of String)().ToArray()
        ' Otherwise:
        Dim array(mySetting.Count - 1) As String
        mySetting.CopyTo(array, 0)
        Return array
    Else
        Dim strEmpty() As String = Enumerable.Empty(Of String).ToArray
        Return strEmpty
    End If
End Function
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
    ' Fires at the beginning of each request
    Dim origin As String = sender.Request.Headers("Origin")
    If origin IsNot Nothing Then
        Dim originURI As Uri = New Uri(origin)
        Dim requestHost As String = originURI.Scheme + Uri.SchemeDelimiter + originURI.Host
        If originURI.Port <> 80 Then
            requestHost += ":" + originURI.Port.ToString
        End If
        If allowedOrigins Is Nothing Then
            allowedOrigins = GetAllowedOrigins()
        End If
        If allowedOrigins IsNot Nothing AndAlso allowedOrigins.Contains(requestHost) Then
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", requestHost)
            If HttpContext.Current.Request.HttpMethod = "OPTIONS" Then
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With")
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000")
                HttpContext.Current.Response.End()
            End If
        End If
    End If
End Sub

我发现的另一个警告是,我需要在Access-Control-Allow-Headers头中添加X-Requested-With值。如果你得到一个CORS错误,检查头,看看你是否需要额外的选项。

我希望这能帮助到那些可能因为这个太常见的问题而感到沮丧的人。