为 Cowboy REST API 启用 CORS

Posted

技术标签:

【中文标题】为 Cowboy REST API 启用 CORS【英文标题】:Enabling CORS for Cowboy REST API 【发布时间】:2013-03-15 00:09:06 【问题描述】:

如何为牛仔休息处理程序启用 CORS? 我尝试添加 options/2 方法,如下所示:

options(Req, State) ->
    [
       <<"access-control-allow-origin">>, <<"*">>,
       <<"access-control-allow-methods">>, <<"GET, OPTIONS">>
     ], Req, State.

但这会导致如下错误:

Error in process <0.263.0> with exit value: case_clause,[<<27 bytes>>,<<1 byte>>,<<28 bytes>>,<<12 bytes>>],http_req,#Port<0.2636>,ranch_tcp,keepalive,<0.263.0>,<<7 bytes>>,1,1,127,0,0,1,56522,<<9 bytes>>,undefined,9090,<<8 bytes>>,undefined,<<0 bytes>>,undefined,<<0 bytes>>,[],[<<4 bytes>>,<<14 bytes>>,<<10 bytes>>,<<74 bytes>>,<<6 bytes>>,<<63 bytes>>,<<15 bytes>>,<<14 bytes>>,<<15 bytes>>,<<13 bytes>>,<<6 bytes>>,<<4 bytes>>,<<29 bytes>>,<<3 bytes>>,<<30 bytes>>,<<16 bytes>>,<<10 bytes>>,<<10 bytes>>],[<<10 bytes>>,[<<10 bytes>>]],undefined,[],waiting,undefined,<<0 bytes>>,false,waiting,[],<<0 bytes>>,undefined,undefined...

我的错误在哪里?

【问题讨论】:

【参考方案1】:

Cowboy 文档说您需要使用 set_resp_headers 设置标头,而不是返回标头列表:

 %% If you need to add additional headers to the response at this point,
 %% you should do it directly in the options/2 call using set_resp_headers.

所以你的代码应该是这样的:

options(Req, State) ->
    Req1 = cowboy_req:set_resp_header(<<"access-control-allow-methods">>, <<"GET, OPTIONS">>, Req),
    Req2 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<"*">>, Req1),
    ok, Req2, State.

你可以测试

curl -H "Origin: http://example.com" \
  -H "Access-Control-Request-Method: GET" \
  -H "Access-Control-Request-Headers: X-Requested-With" \
  -X OPTIONS --verbose \
http://localhost:8080
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r     zlib/1.2.5
> Host: localhost:8080
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: GET
> Access-Control-Request-Headers: X-Requested-With
>
< HTTP/1.1 200 OK
< connection: keep-alive
< server: Cowboy
< date: Mon, 25 Mar 2013 15:59:11 GMT
< content-length: 0
< access-control-allow-methods: GET, OPTIONS
< access-control-allow-origin: *
<
* Connection #0 to host localhost left intact
* Closing connection #0

【讨论】:

options/2 应该在哪里实现?文档并不清楚我们应该在哪个文件中实现它。【参考方案2】:

我的 REST 仅在提供令牌的情况下才允许访问资源。所以我必须实现is_authorized/2 回调,这对于 OPTIONS 和 GET 请求是不同的。见下文:

1) 如下实现options/2

options(Req0, State) ->
  % cors
  Req1 = cowboy_req:set_resp_header(
    <<"access-control-allow-methods">>, <<"GET, OPTIONS">>, Req0),
  Req2 = cowboy_req:set_resp_header(
    <<"access-control-allow-origin">>, <<"*">>, Req1),
  Req3 = cowboy_req:set_resp_header(
    <<"access-control-allow-headers">>, <<"authorization">>, Req2),
  ok, Req3, State.

2) 如下实现is_authorized/2

is_authorized(Req, State) -> 
  case cowboy_req:method(Req) of
    <<"GET">> ->
      case cowboy_req:parse_header(<<"authorization">>, Req) of
        bearer, <<Token/binary>> ->
          true, Req, Token;
        _ ->
          false, <<"Basic realm=\"cowboy\"">>, Req, State
      end;
    <<"OPTIONS">> ->
      true, Req, State
  end.

3)to_json/2方法发送access-control-allow-origin

to_json(Req0, State) ->
  R = # foo => [<<"bar">>] ,
  % cors
  Req1 = cowboy_req:set_resp_header(
      <<"access-control-allow-origin">>, <<"*">>, Req0),
  Body = jsx:encode(R),
  Body, Req1, State.

【讨论】:

【参考方案3】:

你可以试试这个:

Req1 = cowboy_req:set_resp_header(<<"access-control-allow-origin">>, <<"*">>, Req0 -> Worked for me.

【讨论】:

这个问题已有 6.5 年历史,具有高质量的公认答案。此提供的答案被标记为低质量帖子以供审核。以下是How do I write a good answer? 的一些指南。提供的这个答案可能是正确的,但它可以从解释中受益。仅代码答案不被视为“好”答案。来自review。【参考方案4】:

除了这里找到的答案之外,Cowboy 2.* 似乎还有一个中间件功能,您可以使用它来拦截对服务器的所有 REST 处理程序请求,并添加所需的响应标头: https://ninenines.eu/docs/en/cowboy/2.7/guide/middlewares/

可以在这个 Github gist 中找到一个示例 CORS 中间件实现: https://github.com/ninenines/cowboy/blob/master/examples/markdown_middleware/src/markdown_middleware_app.erl

【讨论】:

以上是关于为 Cowboy REST API 启用 CORS的主要内容,如果未能解决你的问题,请参考以下文章

为 REST API 启用 CORS 支持

在 JIRA REST API 中启用 CORS

如何在我的 Django REST api 上调试启用 CORS

使用 Spring MVC 为所有 API 启用 CORS

无法在 Spring Boot Data Rest 中启用 CORS

启用 CORS 从 Cordova 应用程序调用 JSON REST