解决方法:被CORS策略阻止:所请求的资源上没有“ Access-Control-Allow-Origin”标头

时间:2018-12-30 02:25:27

标签: javascript cors salesforce apex visualforce

我正在尝试使用visualforce(VF)页面调用javascript。在VF页面中单击预览时,将执行方法。但是,当我尝试查看Google chrome中的响应“被CORS策略阻止:所请求的资源上没有'Access-Control-Allow-Origin'标头”时出现错误。下面是VF的示例代码。

class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]

def index
 @posts = Post.all
end


def show
end


def new
 @post = Post.new

end


def create

  @post = Post.create(post_params) do |u|
  u.ip_address = request.location
  byebug
end

redirect_to post_path(@post)
end


def edit
@place = Place.find(params[:id])
 end

def update
@post.update(post_params)


redirect_to post_path(@post)
end


def destroy
@post.delete(post_params)
redirect_to posts_path
end



private

def find_post
 @post = Post.find(params[:id])
end

 def post_params
 params.require(:post).permit(:title, :description, :ip_address)
 end

 end

顶点控制器类:

<apex:page controller="calljavascript_cls" >

<script>

function func()

{

var request = new XMLHttpRequest();

request.open('GET', 'https://copado.herokuapp.com/json/v1/webhook/metadata/AAAA?api_key=BBBB');

request.onreadystatechange = function () {
if (this.readyState === 4) {
console.log('Status:', this.status);
console.log('Headers:', this.getAllResponseHeaders());
console.log('Body:', this.responseText);
}
};

 request.send();


 }

 </script>

 <apex:outputText value="{!callfunc}" escape="false"></apex:outputText>

  </apex:page>

我尝试了下面链接中给出的解决方案。无法正常工作。

Link1 Link2

不知道如何进行。请帮助我。

2 个答案:

答案 0 :(得分:0)

您需要根据请求设置标头“ Access-Control-Allow-Headers”,并且服务器还必须允许设置标头“ Access-Control-Allow-Headers”,同时调用CORS

request.setRequestHeader('Access-Control-Allow-Headers', '*');

答案 1 :(得分:0)

我认为这可行。 该答案涉及很多领域,因此分为三个部分:

  • 如何使用CORS代理解决“无访问控制-允许-来源标头”问题
  • 如何避免CORS飞行前检查
  • 如何解决“ Access-Control-Allow-Origin标头不能为通配符” 问题

如何使用CORS代理解决“无访问控制-允许-来源标头” 问题

如果您不控制服务器,您的前端JavaScript代码正在向其发送请求,并且该服务器的响应问题仅在于缺少必要的Access-Control-Allow-Origin标头,您仍然可以通过CORS代理发出请求来工作。为了展示它是如何工作的,首先这里是一些不使用CORS代理的代码:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

catch块被击中的原因是,浏览器阻止该代码访问来自https://example.com的响应。而浏览器这样做的原因是,响应缺少Access-Control-Allow-Origin响应标头。

现在,这是完全相同的示例,只是在其中添加了CORS代理:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

注意:如果在尝试时https://cors-anywhere.herokuapp.com宕机或不可用,请参见下文,了解如何在2中在Heroku上部署自己的CORS Anywhere服务器3分钟。

上面的第二个代码段可以成功访问响应,因为获取请求URL并将其更改为https://cors-anywhere.herokuapp.com/https://example.com(通过在其前面加上代理URL前缀)会导致请求通过该代理发出,然后该请求通过:

  1. 将请求转发到https://example.com
  2. https://example.com接收响应。
  3. Access-Control-Allow-Origin标头添加到响应中。
  4. 将带有添加的标头的响应传递回请求的前端代码。

然后,浏览器允许前端代码访问响应,因为带有Access-Control-Allow-Origin响应标头的响应就是浏览器看到的。

您可以使用https://github.com/Rob--W/cors-anywhere/中的代码轻松运行自己的代理。
您还可以使用5条命令在2-3分钟内轻松地将自己的代理部署到Heroku,

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

运行这些命令后,您将最终运行自己的CORS Anywhere服务器,例如,该服务器运行在https://cryptic-headland-94862.herokuapp.com/。因此,与其为您的请求URL加上https://cors-anywhere.herokuapp.com前缀,不如为您自己的实例的URL前缀;例如https://cryptic-headland-94862.herokuapp.com/https://example.com

因此,如果您尝试使用https://cors-anywhere.herokuapp.com时,发现它已关闭(有时会出现这种情况),然后考虑获得一个Heroku帐户( (如果您还没有),请花2到3分钟的时间完成上述步骤,以在Heroku上部署自己的CORS Anywhere服务器。

无论您是自己运行还是使用https://cors-anywhere.herokuapp.com或其他开放式代理,即使该请求是触发浏览器执行CORS预检OPTIONS请求的请求,该解决方案都将起作用,因为在这种情况下,代理还会发回使预检成功所需的Access-Control-Allow-HeadersAccess-Control-Allow-Methods标头。


如何避免CORS起飞前

问题中的代码会触发CORS预检-因为它发送了Authorization标头。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

即使没有,Content-Type: application/json标头也将触发预检。

“预检”的含义是:浏览器在问题代码中尝试使用POST之前,它将首先向服务器发送OPTIONS请求-确定服务器是否选择了-接收包含POSTAuthorization标头的跨域Content-Type: application/json

  

在一个小的curl脚本上它可以很好地工作-我得到了数据。

要使用curl进行正确测试,您需要模拟浏览器发送的预检OPTIONS请求:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

…将https://the.sign_in.url替换为您实际的sign_in网址。

浏览器需要从OPTIONS请求中看到的响应中必须包含这样的标头:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

如果OPTIONS响应中不包含这些标头,则浏览器将在那里停止,甚至不会尝试发送POST请求。另外,响应的HTTP状态代码必须为2xx,通常为200或204。如果是其他任何状态代码,浏览器将在那里停止。

问题中的服务器正在使用501状态代码响应OPTIONS请求,这显然意味着它试图表明它未实现对OPTIONS请求的支持。在这种情况下,其他服务器通常会以405“不允许使用方法”状态代码进行响应。

因此,如果服务器以405或501或其他任何方式响应该POST请求,您将永远无法从前端JavaScript代码直接向该服务器发出OPTIONS请求200或204,或者它没有响应那些必需的响应头。

避免触发该问题的事前检查的方法是:

  • 如果服务器不需要Authorization请求标头,而是(例如)依靠嵌入在POST请求正文中或作为查询参数的身份验证数据
  • 如果服务器不需要POST主体具有Content-Type: application/json媒体类型,而是将POST主体作为application/x-www-form-urlencoded接受,且其参数名为{{1 }}(或其他),其值为JSON数据

如何解决“ Access-Control-Allow-Origin标头不能为通配符” 问题

  

我收到另一条错误消息:

     
    

响应中“ Access-Control-Allow-Origin”标头的值     当请求的凭据模式为时,不得为通配符'*'     '包括'。因此,不允许原点“ http://127.0.0.1:3000”     访问。发起的请求的凭据模式     XMLHttpRequest由withCredentials属性控制。

  

对于包含凭据的请求,如果json响应标头的值为Access-Control-Allow-Origin,浏览器将不允许您的前端JavaScript代码访问响应。相反,在这种情况下,该值必须与前端代码的来源*完全匹配。

请参阅MDN HTTP访问控制(CORS)文章中的Credentialed requests and wildcards

如果您控制要向其发送请求的服务器,那么处理这种情况的一种常用方法是将服务器配置为采用http://127.0.0.1:3000请求标头的值,并将其回显/反射回Origin响应标头的值。例如,使用nginx:

Access-Control-Allow-Origin

但这只是一个例子;其他(网络)服务器系统提供了类似的方法来回显原始值。


  

我正在使用Chrome。我也尝试过使用Chrome CORS插件

该Chrome CORS插件显然只是将add_header Access-Control-Allow-Origin $http_origin 标头注入浏览器看到的响应中。如果插件更聪明,它将要执行的操作是将假Access-Control-Allow-Origin: *响应标头的值设置为前端JavaScript代码Access-Control-Allow-Origin的实际来源。

因此,即使用于测试,也请避免使用该插件。这只是分心。如果您想测试从服务器获得的响应,而没有浏览器对其进行过滤,则最好使用上述的http://127.0.0.1:3000


关于问题中curl -H请求的前端JavaScript代码:

fetch(…)

删除这些行。 headers.append('Access-Control-Allow-Origin', 'http://localhost:3000'); headers.append('Access-Control-Allow-Credentials', 'true'); 标头是 response 标头。您永远不想在请求中发送它们。唯一的效果就是触发浏览器进行预检。