【问题标题】:How to add cross domain support to WCF service如何向 WCF 服务添加跨域支持
【发布时间】:2012-12-12 10:25:53
【问题描述】:

我试图允许 POST 请求来自托管在 localhost:80 的我的 javascript 应用程序到托管在不同端口的 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>

【问题讨论】:

  • 如果您描述“但不知何故它不起作用”是什么意思,那将是一个很好的问题。
  • 我已经更新了描述。

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


【解决方案1】:

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

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

【讨论】:

  • 这几乎就是我所需要的。但是我注意到,如果您将数据作为 json 发送,它就不起作用。您的示例页面使用 POST 请求发送单个字符串值。或者可能是因为 ExtJS 处理请求数据的方式与 jQuery 不同:/
  • 该示例确实以 JSON 格式发送数据 - POST / PUT 方法的输入是 JSON 字符串(请注意,输入包含在 " 中)。它也适用于对象,只是在示例中操作将字符串作为参数。
  • 我已将测试页面中的数据更改为 `var data = { foo: "bar" };` 我收到 400 Bad request。我检查了日志,但那里没有任何帮助。那么我是否应该对 WCF 本身进行更改以支持它?
  • 是的——在这种情况下,操作必须采用用户类型的参数,并带有一个名为“foo”的字符串字段。如果操作需要一个字符串,你应该传递一个字符串;如果操作将对象作为参数,则只能传递对象。
  • 它不应该是Object,而是映射到您要发送的 JSON 的数据类型。例如,如果您的类有一个名为“Foo”的字符串属性,它应该能够接受 JSON 对象 {"Foo":"hello world"} 作为输入。
【解决方案2】:

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

【讨论】:

  • 非常适合我!
【解决方案3】:

这对我来说比 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

【讨论】:

  • 非常感谢!找了好久终于得到了准确的解决方案。
  • 很好,经过几个小时的研究,这终于奏效了。谢谢兄弟!
  • 您节省了我大量的研发时间。我真的很想投票不止一次:)..谢谢
  • 像往常一样:使用的命名空间是什么?请具体一点,因为我的 VS 没有找到它本身。
  • 命名空间:System.Web
【解决方案4】:

以下 .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" , "*");
    }
}

【讨论】:

    【解决方案5】:

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

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

    我希望这有助于其他可能对这个太常见的问题感到沮丧的人。

    【讨论】:

      猜你喜欢
      • 2012-03-07
      • 2012-04-30
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 2011-06-11
      • 2019-06-16
      • 1970-01-01
      相关资源
      最近更新 更多