65.9K
CodeProject 正在变化。 阅读更多。
Home

在 WCF 中启用 CORS

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (25投票s)

2014年11月24日

CPOL

5分钟阅读

viewsIcon

167576

downloadIcon

3720

在 WCF 中启用 CORS 支持,并允许从不同域(而非 WCF 托管域)消费 WCF 作为 REST API。

引言

这是一个中级的 WCF REST 解决方案示例,并启用了 CORS 访问,以便可以从其他域消费此 WCF 服务,而不会出现跨域问题。我将在后面的部分更详细地解释 CORS,所以请稍候,阅读完问题和解决方案。在开发类似解决方案时,我遇到了一些问题,没有找到任何有用的工作文章/博客,因此我在这里发布此文。希望这能有所帮助。

背景

我们开发 WCF 服务作为 REST 服务,并使用 JavaScript 和 jQuery 调用来消费它。这对于从单页应用程序或纯 JavaScript 应用程序开始是很好的。只要 WCF 服务托管域与消费服务的域相同,您就不会遇到任何问题。问题出现在,当您开始允许其他公司将 WCF 服务作为 REST 服务进行消费时。例如,您有一个报告服务,并将其公开为 REST 服务。您有一个 Web 门户,其中消费了该服务。由于它是纯 REST 式的,因此您希望允许第三方公司消费基于 REST 的服务并在其网站上显示相同的报告。注意:在这种情况下,用于消费 WCF 服务的 JS 将位于客户端域,而 WCF 域将是您的域。这两个不同的域将导致跨域问题,即 WCF 在调用时会抛出错误。

使用代码

在直接深入代码之前,我想正式介绍一下 REST 和 CORS 问题。

表述性状态转移 (REST) 是万维网的架构抽象;更准确地说,REST 是一种架构风格,它包含一组协调的架构约束,应用于分布式超媒体系统中的组件、连接器和数据元素。REST 忽略组件实现和协议语法的细节,而是关注组件的角色、它们与其他组件交互的约束以及它们对重要数据元素的解释。-- http://en.wikipedia.org/wiki/Representational_state_transfer#Framework_implementations

跨源资源共享 - 用户代理通常会对网络请求应用同源策略。这些限制阻止了来自一个源的客户端 Web 应用程序获取从另一个源检索到的数据,并且还限制了可以自动向与运行应用程序源不同的目标发出的不安全 HTTP 请求。- http://www.w3.org/TR/cors/#introduction

在此示例中,我将使用 Visual Studio 提供的示例 WCF 服务。首先,我们将创建一个 WCF REST 服务,它可以接受带有对象参数的 POST 请求。编写一个简单的基于 JS 的应用程序来消费它。WCF 服务将简单地返回一些前缀 + 收到的对象值。由于我们主要关注启用 CORS,因此我将其保持非常基础。

然后,我将向您展示错误究竟发生在何处。之后,将提供克服 CORS 问题的解决方案。

步骤#1。让我们创建一个 WCF 服务项目,按照如下方式创建服务契约和操作契约。

[ServiceContract]
public interface IService1
{

    [OperationContract]
    [WebInvoke(UriTemplate = "/TestMethod", Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json
      )]
    string TestMethod(CompositeType value);

}

步骤#2 CompositeType 的定义是 -

[DataContract]
public class CompositeType
{
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue
    {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
        get { return stringValue; }
        set { stringValue = value; }
    }
}

步骤#3 然后,创建服务类。以下是代码。

public class Service1 : IService1

{
    public string TestMethod(CompositeType value)
    {
        return string.Format("You entered: {0}", value.StringValue);
    }
}

步骤#4 假设它已托管在某处 (www.example1.com),并使用 Fiddler 进行测试是否正常工作。以下是结果。

 

太棒了!它工作正常,请参见结果 - 状态码 200。

步骤#5 我有一个简单的 JavaScript (将在 HTML 文件中),用于调用此基于 REST 的方法。该 HTML 文件托管在 - https://。HTML 文件中 JavaScript 部分的源代码。

$(document).ready(function () {
	$("button").click(function () {
		alert("clicked");
		var data = $("#txt").val();
		var postdata = {};
		var data_obj = {"BoolValue" : "true" , "StringValue": data}
		postdata["value"] =  data_obj; 

		var url = "https://tmdev01.tm00.com/testwcf/service1.svc/TestMethod";
		$.ajax({
			type: "POST",
			url: url,
			contentType: "application/json; charset=utf-8",
			data: JSON.stringify(postdata),
			dataType: "json",
			success: function(data) {console.log(data);},
			error: function(a,b,c) {console.log(a);}
		});
	});
});

-----------------HTML 部分-------------

Enter something <input id="txt" type="text" /><button>Get WCF data</button>

现在,当我执行此 JavaScript 时,它将抛出错误。以下是浏览器控制台中的错误消息。

引用

 OPTIONS https://www.example.com/wcfv1/service1.svc/TestMethod 
test1.html:1 XMLHttpRequest 无法加载 https://www.example1.com/wcfv1/service1.svc/TestMethod。请求的资源上没有 'Access-Control-Allow-Origin' 标头。因此,源 'https://' 不允许访问。响应的 HTTP 状态码为 405。

以下是浏览器请求有效负载信息。

 

它不再与其他域的 JavaScript 调用一起工作了 : ( 。

仔细查看,我们是用“POST”请求调用 WCF,但它显示请求方法为“OPTIONS”。这是因为 POST、PUT、DELETE 方法是不安全的方法,而跨域请求首先会发送一个预检请求,即 **OPTIONS** 请求,以查看它是否成功,意味着服务器响应/发送了 OK 信号,然后才会再次发送实际的 POST 请求。

另外,请注意,它会发送各种请求标头,例如“Access-Control-Request-Headers", "Access-Control-Request-Method".

这意味着什么? - 我们作为 WCF 服务开发人员需要响应该 OPTIONS HTTP 请求。

如何做到? - 添加 global.asax 文件并将以下代码添加到 Application_BeginRequest。以下是代码片段。

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "https://");
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}

如上所述,我允许了“https://”的源,这样如果 JavaScript 放置在该域上并且正在调用 WCF,它将被允许。我还添加了我们应该在 OPTIONS 请求标头中发送的请求响应标头。

这是一个极其重要的决定: - 您始终可以使用 **“*”** 作为 *Access-Control-Allow-Origin*,但出于安全原因,不建议这样做。因为您正在向所有人开放从任何地方调用您的 WCF 服务器作为 REST 服务的权限。而您应该知道,您正在为谁提供 CORS 访问权限,并将这些域放在此处。

这是我在这里做的基本工作,您可以将其配置化。

这样我们就完成了此设置,我将部署此解决方案,看看是否会有帮助。

结论

现在,我使用与上面相同的 JavaScript,只是将更改后的 WCF 代码托管到另一个虚拟目录(*testwcf*)。因此,当我发出 ajax 请求时,请注意它发出了 2 个请求 - **OPTIONS, POST**。请参见下面的屏幕截图。

 

我们将分析这两个请求的详细信息,因此,首先,请看 **OPTIONS** 请求的响应,以及它与第一次尝试非 CORS WCF 有何不同。

 

如您所见,现在我们的 WCF 服务响应了所有必需的响应标头,例如“*access-control-allow-*”。- 注意:我们在 global.asax 中完成了这些。

因此,当此请求成功时,浏览器会发出第二个请求,即实际的 **POST**。让我们检查一下它的详细信息。

 

现在,您可以看到它实际上发出了请求有效负载,并查看响应标头(请参阅状态码 - 200 OK),它成功了并且具有一些 content-length。

关注点

如果您觉得有趣并且有一些建议,请发表评论,我随时与您互动。

在此处下载源代码。

 

© . All rights reserved.