我试图允许从托管在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>
这对我来说比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错误,检查头,看看你是否需要额外的选项。
我希望这能帮助到那些可能因为这个太常见的问题而感到沮丧的人。