前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯云SCF + 腾讯云API网关实现跨域

腾讯云SCF + 腾讯云API网关实现跨域

原创
作者头像
孔令飞
修改2019-11-01 12:28:58
16.6K1
修改2019-11-01 12:28:58
举报
文章被收录于专栏:ServerlessServerless

跨域介绍

跨来源资源共享(Cross-Origin Resource Sharing(CORS))是一种使用额外 HTTP 标头来让目前浏览网站的 user agent 能获得访问不同来源(网域)服务器特定资源之权限的机制。当 user agent 请求一个不是目前文件来源——来自于不同网域(domain)、通信协定(protocol)或通信端口(port)的资源时,会建立一个跨来源HTTP请求(cross-origin HTTP request)。

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域:

当前页面 url

被请求页面 url

是否跨域

原因

http://www.example.com/

http://www.example.com/index.html

同源(协议、域名、端口号相同)

http://www.example.com/

https://www.example.com/index.html

跨域

协议不同(http/https)

http://www.example.com/

http://www.baidu.com/

跨域

主域名不同(example/baidu)

http://www.example.com/

http://blog.example.com/

跨域

子域名不同(www/blog)

http://www.example.com:8080/

http://www.example.com:7001/

跨域

端口号不同(8080/7001)

跨域种类

一共有 2 种跨域请求:

  1. 简单请求
  2. 预检请求

简单请求

当 HTTP 请求出现以下两种情况时,浏览器认为是简单跨域请求:

  1. 请求方法是 GET、HEAD 或者 POST,并且当请求方法是 POST 时,Content-Type 必须是 application/x-www-form-urlencoded, multipart/form-data 或着 text/plain 中的一个值。
  2. 请求中没有自定义 HTTP 头部。

对于简单跨域请求,浏览器要做的就是在 HTTP 请求中添加 Origin Header,将 JavaScript 脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加 Access-Control-Allow-Origin Header。浏览器收到响应后,查看 Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给 JavaScript。否则浏览器忽略此次响应。相较于同源请求,CORS 简单请求会在头信息中额外增加一个 Origin 字段。

预检请求

当 HTTP 请求出现以下两种情况时,浏览器认为是带预检(Preflighted)的跨域请求:

  1. 除 GET、HEAD 和 POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他 HTTP 方法。
  2. 请求中出现自定义 HTTP 头部。

非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为"预检"请求(preflight)。

预检(preflighted)请求会先用 HTTP 的 OPTIONS 方法请求另一个域名资源,确认后续实际(actual)请求能否可安全送出。由于跨域请求可能会携带使用者的信息,所以要先进行预检请求。

腾讯云SCF + 腾讯云API 网关实现跨域

当 SCF 绑定 API 网关触发器后,有 2 种方式实现跨域**(建议使用第 1 种方法)**:

  1. 借助 API 网关的跨域功能
  2. 云函数中实现跨域逻辑

本文就来介绍下,如果通过这 2 种方式,来实现跨域功能。建议选择第 1 种方式,来实现跨域功能,这样用户就不需要在函数中实现跨域相关的逻辑代码。

借助 API 网关的跨域功能

Step1. 绑定 API 网关触发器

绑定 API 网关触发器:

  1. 请求方法:GET/POST/HEAD/PUT/DELETE(根据需要进行选择)

目前 API 网关当请求方法为 ANY 时,无法开启跨域功能,所以这里请求方法不能选择 ANY

Step2. 在 API 网关产品页面,开启 API 的跨域功能

1、在 API网关 产品页面,选择绑定的 API 服务和绑定的 API,编辑 API:

2、在编辑页面开启:支持CORS 选项

3、保存设置后,发布 API

Step3. 测试跨域功能

1、简单跨域请求

代码语言:javascript
复制
$ curl -v -X GET -H "Origin: http://example.com"  http://service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com/test/corstest
* About to connect() to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com port 80 (#0)
*   Trying 111.231.97.251...
* Connected to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com (111.231.97.251) port 80 (#0)
> GET /test/corstest HTTP/1.1
> User-Agent: curl/7.29.0
> Host: service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com
> Accept: */*
> Origin: http://example.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Api-ID: api-bzh0ss12
< X-Service-RateLimit: 5000/5000
< X-Api-RateLimit: unlimited
< Date: Thu, 31 Oct 2019 23:25:46 GMT
< Access-Control-Allow-Origin: http://example.com
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type
<
* Connection #0 to host service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com left intact
cors template function run successfully

2、预检跨域请求

代码语言:javascript
复制
$ curl -v -X OPTIONS -H "Access-Control-Request-Method: GET" http://service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com/test/corstest
* About to connect() to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com port 80 (#0)
*   Trying 111.231.97.251...
* Connected to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com (111.231.97.251) port 80 (#0)
> OPTIONS /test/corstest HTTP/1.1
> User-Agent: curl/7.29.0
> Host: service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com
> Accept: */*
> Access-Control-Request-Method: GET
>
< HTTP/1.1 204 No Content
< Date: Thu, 31 Oct 2019 23:24:53 GMT
< Connection: keep-alive
< X-Api-ID: api-bzh0ss12
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type
< Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
< Access-Control-Max-Age: 86400
< Server: apigw/1.0.15
<
* Connection #0 to host service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com left intact

可以看到,网关均正确返回跨域需要的 Headers。

开启跨域后,OPTIONS 请求不走鉴权逻辑

云函数中实现跨域逻辑

Step1. 创建带跨域逻辑的云函数

创建函数:

  1. 运行环境: Python2.7
  2. 选择 空白模板
  3. 执行方法: index.main_handle

函数代码为:

代码语言:javascript
复制
# -*- coding: utf-8 -*-

import logging

logger = logging.getLogger()

response = {"isBase64": False, "statusCode": 200, "headers": {}}

def set_simple_cors():
    response["headers"]["Access-Control-Allow-Origin"] = "*"
    response["headers"]["Access-Control-Allow-Credentials"] = "true"
    response["headers"]["Access-Control-Expose-Headers"] = "X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type"
    return response

def set_preflight_cors():
    response["headers"]["Access-Control-Allow-Origin"] = "*"
    response["headers"]["Access-Control-Allow-Credentials"] = "true"
    response["headers"]["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH"
    response["headers"]["Access-Control-Allow-Headers"] = "X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type"
    response["headers"]["Access-Control-Max-Age"] = 86400
    return response

def is_simple_cors(httpMethod, headers):
    simple_methods = ['HEAD', 'GET', 'POST']
    simple_headers = ['accept', 'accept-language', 'content-language', 'last-event-id', 'content-type', 'origin']
    allow_content_types = ['application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']

    if httpMethod not in simple_methods:
        return False

    if httpMethod == "POST" and headers['content-type'] not in allow_content_types:
        return False

    for header in headers:
        if header not in simple_headers:
            return False

    return True

def is_preflight_cors(httpMethod, headers):
    httpPreflightMethod = headers.get('access-control-request-method', '')
    if httpMethod == "OPTIONS" and httpPreflightMethod != "":
        return True

    return False

def main_handler(event, context):
    logger.info('start cors template function')
    logger.info("event: %s", event)

    httpMethod = event['httpMethod']
    headers = event['headers']

    # 跨域:预检请求
    if is_preflight_cors(httpMethod, headers):
        logger.info("receive preflight http cors request")
        return set_preflight_cors()

    # 跨域:简单请求
    if is_simple_cors(httpMethod, headers):
        logger.info("receive simple http cors request")
        set_simple_cors()

    # 同源请求,正常完成业务逻辑
    logger.info("receive normal http request.")
    body = "cors template function run successfully"
    response["body"] = body
    return response

Step2. 绑定 API 网关触发器

绑定 API 网关触发器:

  1. 请求方法: ANY
  2. 开启 启用集成响应

开启集成响应后,返回的参数需要满足集成响应的格式。

参考文档:集成响应与透传响应 - https://cloud.tencent.com/document/product/583/12513#.E9.9B.86.E6.88.90.E5.93.8D.E5.BA.94.E4.B8.8E.E9.80.8F.E4.BC.A0.E5.93.8D.E5.BA.94

Step3. 测试跨域功能

1、简单跨域请求

代码语言:javascript
复制
$ curl -v -X GET -H "Origin: http://example.com" http://service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com/test/corstest
* About to connect() to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com port 80 (#0)
*   Trying 111.231.97.251...
* Connected to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com (111.231.97.251) port 80 (#0)
> GET /test/corstest HTTP/1.1
> User-Agent: curl/7.29.0
> Host: service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com
> Accept: */*
> Origin: http://example.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Api-ID: api-51u298ne
< X-Service-RateLimit: 5000/5000
< X-Api-RateLimit: unlimited
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type
< Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
< Access-Control-Allow-Origin: *
< Date: Fri, 01 Nov 2019 00:08:50 GMT
<
* Connection #0 to host service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com left intact
cors template function run successfully

2、 预检跨域请求

代码语言:javascript
复制
$ curl -v -X OPTIONS -H "Access-Control-Request-Method: GET" http://service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com/test/corstest
* About to connect() to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com port 80 (#0)
*   Trying 111.231.97.251...
* Connected to service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com (111.231.97.251) port 80 (#0)
> OPTIONS /test/corstest HTTP/1.1
> User-Agent: curl/7.29.0
> Host: service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com
> Accept: */*
> Access-Control-Request-Method: GET
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Api-ID: api-51u298ne
< X-Service-RateLimit: 5000/5000
< X-Api-RateLimit: unlimited
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: X-Api-ID,X-Service-RateLimit,X-UsagePlan-RateLimit,X-UsagePlan-Quota,Cache-Control,Connection,Content-Disposition,Date,Keep-Alive,Pragma,Via,Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cookie,Expect,From,Host,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range,Origin,Referer,User-Agent,X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Accept-Range,Age,Content-Range,Content-Security-Policy,ETag,Expires,Last-Modified,Location,Server,Set-Cookie,Trailer,Transfer-Encoding,Vary,Allow,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-Type
< Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
< Access-Control-Allow-Origin: *
< Date: Fri, 01 Nov 2019 00:07:40 GMT
<
* Connection #0 to host service-4mlv1c3l-1253970226.ap-shanghai.apigateway.myqcloud.com left intact

可以看到,函数均正确返回跨域需要的 Headers。

API 网关后期产品优化

目前 ANY 方法还不支持跨域设置,这个 API 网关后期会考虑支持。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 跨域介绍
  • 跨域种类
    • 简单请求
      • 预检请求
      • 腾讯云SCF + 腾讯云API 网关实现跨域
        • 借助 API 网关的跨域功能
          • Step1. 绑定 API 网关触发器
          • Step2. 在 API 网关产品页面,开启 API 的跨域功能
          • Step3. 测试跨域功能
        • 云函数中实现跨域逻辑
          • Step1. 创建带跨域逻辑的云函数
          • Step2. 绑定 API 网关触发器
          • Step3. 测试跨域功能
      • API 网关后期产品优化
      相关产品与服务
      API 网关
      腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档