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

Azure WCF REST 服务 – CORS 问题

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2016年7月19日

CPOL

3分钟阅读

viewsIcon

9884

什么是 CORS? 当一个资源请求来自与该资源自身所服务的域不同的域的资源时,该资源会发出跨域 HTTP 请求。例如,从 http://domain-a.com 提供的 HTML 页面会向 http://domain-b.com/image.jpg 发出 <img> src 请求。

什么是 CORS?

当一个资源请求来自与该资源自身所服务的域不同的域的资源时,该资源会发出跨域 HTTP 请求。例如,从 http://domain-a.com 提供的 HTML 页面会向 http://domain-b.com/image.jpg 发出 <img> src 请求。如今,Web 上的许多页面都会从不同的域加载 CSS 样式表、图像和脚本等资源。

出于安全原因,浏览器会限制从脚本中发起的跨域 HTTP 请求。例如,XMLHttpRequest 遵循同源策略。因此,使用 XMLHttpRequest 的 Web 应用程序只能向其自己的域发出 HTTP 请求。为了改进 Web 应用程序,开发人员要求浏览器供应商允许 XMLHttpRequest 发出跨域请求。

W3C Web 应用程序工作组推荐新的 跨域资源共享(CORS) 机制。CORS 为 Web 服务器提供跨域访问控制,从而实现安全的跨域数据传输。现代浏览器在 API 容器(例如 XMLHttpRequest)中使用 CORS 来减轻跨域 HTTP 请求的风险。

它是如何工作的?

跨域资源共享标准通过添加新的 HTTP 标头来工作,这些标头允许服务器描述允许使用 Web 浏览器读取该信息的源集。 此外,对于 GET 以外的 HTTP 方法,或者对于具有某些 MIME 类型的 POST 用法,该规范要求浏览器“预检”请求,使用 HTTP OPTIONS 请求方法征求服务器支持的方法,然后,在获得服务器的“批准”后,使用实际的 HTTP 请求方法发送实际的请求。 服务器还可以通知客户端是否应随请求一起发送“凭据”(包括 Cookie 和 HTTP 身份验证数据)。

来源:Mozilla Developer – CORS

预检错误是什么样的?

1

如果您查看上面的错误,您将得到一个名为“CORS preflight channel did not succeed”(CORS 预检通道未成功)的错误。

我试图解决什么问题?

我正在编写一个应该很容易的 Azure 云服务。我在我的云项目中创建了一个 WCF Web 角色,我的服务接口如下:

[ServiceContract]
    public interface IService1
    {

        #region Getters
        [WebGet(UriTemplate = "/GetUsers",
 RequestFormat = WebMessageFormat.Json,
 ResponseFormat = WebMessageFormat.Json,
 BodyStyle = WebMessageBodyStyle.Bare)]
        string GetUsers();

        [WebGet(UriTemplate = "/GetUser/{Id}")]
        string GetUser(string Id);

        #endregion

        #region Create Methods

        [WebInvoke(Method = "POST", UriTemplate = "/AddUser",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool AddUser(User user);

        [WebInvoke(Method = "POST", UriTemplate = "/AddUserDetails",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool AddUserDetails(UserDetail userdetails);
        #endregion

        #region Update Methods

        [WebInvoke(Method = "PUT", UriTemplate = "/UpdateUser",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool UpdateUser(User user);

        [WebInvoke(Method = "PUT", UriTemplate = "/UpdateUserDetails",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool UpdateUserDetails(UserDetail userdetails);
        #endregion

        #region Delete Methods

        [OperationContract]
        [WebInvoke(Method = "DELETE",
       UriTemplate = "/DeleteUser/{Id}",
       BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        bool DeleteUser(string Id);

        [OperationContract]
        [WebInvoke(Method = "DELETE",
         UriTemplate = "/DeleteUserDetails/{Id}",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        bool DeleteUserDetails(string Id);
        #endregion

        [OperationContract]
        [WebGet(UriTemplate = "/IsValidUser/{username}/{password}",
 RequestFormat = WebMessageFormat.Json,
 ResponseFormat = WebMessageFormat.Json,
 BodyStyle = WebMessageBodyStyle.Bare)]
        string IsValidUser(string username, string password);
    }

这是一个用于对用户和用户详细信息进行 CRUD 操作的服务。服务实现只是调用数据库并使用 LINQ to SQL 执行操作。

在我完成我的实现并尝试在本地检查“POSTMAN”或“Advance REST Client”(两者都是用于处理 REST 服务的工具)后,请求和响应是正确的。 但是在 Azure 中部署后,我得到了如前所示的预检错误。

解决方案

我们必须在请求标头中添加“Access-Control-Allow-Origin”,并要求 WCF REST 服务允许预检请求。有时,根据您的浏览器,“POST” 请求也可能最终成为 “OPTION” 请求。

因此,我们必须按照以下步骤来解决问题:

1. 如果您没有 Global.asax(活动服务器应用程序文件),请在 WCF 服务中添加一个。

2. 如下更新“Application_BeginRequest”方法中的代码:

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("Cache-Control", "no-cache");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET,PUT, OPTIONS, 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();
            }
        }

这样您就可以在请求标头中看到“Access-Control-Allow-Origin”。
一个示例 POSTMAN 请求如下:

2
但是有时 REST 测试客户端会毫无问题地发送请求,但是在使用客户端(在本例中为用于跨平台应用程序开发的 Ionic Framework)时,我们可能仍然会收到错误。

如何实时测试?

我编写了一个简单的 “AJAX” 客户端来测试,如下所示:

<div>
    <select id="method">
<option value="get">POST</option>
<option value="post">GET</option>
<option value="put">PUT</option>
</select>
    <input type="button" value="Try it" onclick="sendRequest()" />
    <span id='value1'>(Result)</span></div>
@section scripts {
    <script>
        // TODO: Replace with the URL of your WebService app
        var serviceUrl = 'http://yoururl.cloudapp.net/Service1.svc/UpdateUser';
        var user =
              {
                  "user":
    {
        "Id": "28",
        "UserName": "K999",
        "Password": "k999",
        "IsAdmin": 1
    }
              }



        function sendRequest() {
            var method = $('#method').val();

            $.ajax({
                type: "PUT",
                url: serviceUrl,
                data: JSON.stringify(user),
                crossDomain: true,
                contentType: "application/json; charset=utf-8", // to the server
                ProcessData: true,
            }).done(function (data) {
                $('#value1').text(data);
            }).error(function (jqXHR, textStatus, errorThrown) {
                $('#value1').text(jqXHR.responseText || textStatus);
            });
        }
    </script>
}

希望对某人有帮助。祝您编码愉快!!


© . All rights reserved.