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

时间:2012-12-26 23:57:22

标签: c# asp.net wcf http-headers cross-domain

我正在尝试允许来自我在localhost:80托管的javascript应用程序的POST请求到托管在不同端口的WCF REStful服务,但不知何故它不起作用。我已经尝试在标题中添加自定义属性,并在我的服务的JSONData方法中以编程方式添加它,但我仍然在我的响应中得到'405 Method not allowed'。这里适当的方法是什么?

这是我的界面:

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>

5 个答案:

答案 0 :(得分:28)

这对我来说比Web.config版本更好:

创建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

答案 1 :(得分:12)

将这些节点添加到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/

答案 2 :(得分:9)

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

我写了一篇关于为WCF添加CORS支持的博客文章。这不是最简单的实现,但希望帖子中的代码可以简单地复制/粘贴到您的项目中。该帖子位于http://blogs.msdn.com/b/carlosfigueira/archive/2012/05/15/implementing-cors-support-in-wcf.aspx

答案 3 :(得分:0)

以下.NET代码(global.asax)有一个重要区别,即代替*,最好回显Origin域,因为这样可以通过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" , "*");
    }
}

答案 4 :(得分:0)

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

我首先通过使用 SpecializedString 集合修改 web.config 文件以包含我允许的源站点,该集合允许将字符串数组存储在 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 错误,请先检查该标题,看看您是否需要其他选项。

我希望这可以帮助其他可能因这个太常见的问题而感到沮丧的人。