参考资料
- 代理集成,https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
- 非代理集成,https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/getting-started-lambda-non-proxy-integration.html
从上一篇文章中我们知道,apigateway集成lambda有两种方式
- 代理集成,集成所有api方法,将请求直接发往lambda函数,无需对api进行重新部署
- 非代理集成,见下文
restapi代理集成lambda
创建lambda函数,取名GetStartedLambdaProxyIntegration,执行角色保持默认即可
下面是官方问demo函数,我们精简一下把校验的逻辑去掉看看(测试没必要)
'use strict';
export const handler = async (event) => {
// 查看事件
console.log(event)
let name = "you";
let city = 'World';
let time = 'day';
let day = '';
let responseCode = 200;
// 打印请求
console.log("request: " + JSON.stringify(event));
// 解析请求参数
let name = event.name === undefined ? 'you' : event.name;
let city = event.city === undefined ? 'World' : event.city;
let time = event.time === undefined ? 'morning' : event.time;
// 解析请求体
let body = JSON.parse(event.body).time
let greeting = `Good ${time}, ${name} of ${city}.`;
let responseBody = {
message: greeting,
input: event
};
// 构造响应
let response = {
statusCode: responseCode,
headers: {
"x-custom-header" : "my custom header value"
},
body: JSON.stringify(responseBody)
};
// 打印响应
console.log("response: " + JSON.stringify(response))
// 返回响应
return response;
};
创建restapi由于resapi是资源和方法的组合,因此有以下
-
资源 /helloworld,不要勾选代理资源
代理集成和代理资源不是一个概念
-
方法 ANY(捕获所有),勾选lambda代理集成
注意这里创建方法并集成lambda后会提示自动为lambda函数添加权限
注意:如果手动创建lambda函数,并不会自动添加这个权限,即apigateway没有权限调用lambda
我们把这个权限删掉查看调用结果
创建完毕后需要手动新型部署,创建并选择test阶段,每个阶段都是一个apigateway的快照
图示实际上就是方法/集成,请求/响应之间的转换,我们可以更精细的设置这些阶段的参数。可以点击测试
在访问控制方面,我们有以下选择
-
匿名访问
-
身份访问
- apigateway基于资源的策略
- iam策略
- lambda授权方
- cognito身份池
终端请求测试,在非中国区是可以直接请求的
// 未备案无法匿名访问
$ curl https://y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn/test/helloworld
{"Message":null}
// 访问错误方法
{"message":"No method found matching route helloworl for http method GET."}
// 没有凭证
{"message": "Missing Authentication Token"}
// 内部错误,可能是apigate无法访问lambda,可以在apigateway控制台测试
{"message": "Internal server error"}
// 不知道为什么始终403
$ awscurl --service execute-api -X GET --region cn-north-1 --profile admin "https://y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn/test/helloworld?name=John&city=Seattle"
{"Message":null}
Traceback (most recent call last):
File "/home/ec2-user/.local/bin/awscurl", line 8, in <module>
sys.exit(main())
File "/home/ec2-user/.local/lib/python3.7/site-packages/awscurl/awscurl.py", line 521, in main
inner_main(sys.argv[1:])
File "/home/ec2-user/.local/lib/python3.7/site-packages/awscurl/awscurl.py", line 515, in inner_main
response.raise_for_status()
File "/home/ec2-user/.local/lib/python3.7/site-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn/test/helloworld?name=John&city=Seattle
后来发现iam认证需要在方法上单独开启,之后仍旧需要部署一下
重新请求
$ awscurl --service execute-api -X GET --region cn-north-1 --profile admin "https://y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn/test/helloworld?name=John&city=Seattle"
{"message":"Good day, John of Seattle.","input":{"resource":"/helloworld","path":"/helloworld","httpMethod":"GET","headers":{"Accept":"application/xml","Accept-Encoding":"gzip, deflate","Content-Type":"application/json","Host":"y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn","User-Agent":"python-requests/2.28.1","x-amz-content-sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","x-amz-date":"20230305T055851Z","X-Amzn-Trace-Id":"Root=1-64042f9b-79563b285727810b2a19fe26","X-Forwarded-For":"52.81.227.226","X-Forwarded-Port":"443","X-Forwarded-Proto":"https"},"multiValueHeaders":{"Accept":["application/xml"],"Accept-Encoding":["gzip, deflate"],"Content-Type":["application/json"],"Host":["y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn"],"User-Agent":["python-requests/2.28.1"],"x-amz-content-sha256":["e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"],"x-amz-date":["20230305T055851Z"],"X-Amzn-Trace-Id":["Root=1-64042f9b-79563b285727810b2a19fe26"],"X-Forwarded-For":["52.81.227.226"],"X-Forwarded-Port":["443"],"X-Forwarded-Proto":["https"]},"queryStringParameters":{"city":"Seattle","name":"John"},"multiValueQueryStringParameters":{"city":["Seattle"],"name":["John"]},"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"wwo9nn","resourcePath":"/helloworld","httpMethod":"GET","extendedRequestId":"BSxgZHB2hTIFSmw=","requestTime":"05/Mar/2023:05:58:51 +0000","path":"/test/helloworld","accountId":"xxxxxxxxxxx","protocol":"HTTP/1.1","stage":"test","domainPrefix":"y2o41dcif7","requestTimeEpoch":1677995931863,"requestId":"38db2a8b-b5d7-4230-a3e4-3851ceada31b","identity":{"cognitoIdentityPoolId":null,"accountId":"xxxxxxxxxxx","cognitoIdentityId":null,"caller":"AIDAQRIBWRJKPC2O5EBGY","sourceIp":"52.81.227.226","principalOrgId":"o-vut99korpx","accessKey":"AKIAQRIBWRJKK45B727U","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws-cn:iam::xxxxxxxxxxx:user/zhaojie","userAgent":"python-requests/2.28.1","user":"AIDAQRIBWRJKPC2O5EBGY"},"domainName":"y2o41dcif7.execute-api.cn-north-1.amazonaws.com.cn","apiId":"y2o41dcif7"},"body":null,"isBase64Encoded":false}}
在cwlogs中查看lambda函数的请求事件如下
- 请求参数和请求体
- 请求发起人,控制台上为
aws-internal/3 aws-sdk-java
{
resource: '/helloworld',
path: '/helloworld',
httpMethod: 'GET',
headers: null,
multiValueHeaders: null,
queryStringParameters: { city: 'Seattle' },
multiValueQueryStringParameters: { city: [ 'Seattle' ] },
pathParameters: null,
stageVariables: null,
requestContext: {
resourceId: 'wwo9nn',
resourcePath: '/helloworld',
httpMethod: 'GET',
extendedRequestId: 'BSlRoGtiBTIFkGQ=',
requestTime: '05/Mar/2023:04:35:22 +0000',
path: '/helloworld',
accountId: 'xxxxxxxxxxx',
protocol: 'HTTP/1.1',
stage: 'test-invoke-stage',
domainPrefix: 'testPrefix',
requestTimeEpoch: 1677990922153,
requestId: '0754dbf7-c943-41cd-8cd0-7f23c24f72e3',
identity: {
cognitoIdentityPoolId: null,
cognitoIdentityId: null,
apiKey: 'test-invoke-api-key',
principalOrgId: null,
cognitoAuthenticationType: null,
userArn: 'arn:aws-cn:iam::xxxxxxxxxxx:user/xxxxxx',
apiKeyId: 'test-invoke-api-key-id',
userAgent: 'aws-internal/3 aws-sdk-java/1.12.407 Linux/5.4.231-145.341.amzn2int.x86_64 OpenJDK_64-Bit_Server_VM/25.362-b10 java/1.8.0_362 vendor/Oracle_Corporation cfg/retry-mode/standard',
accountId: 'xxxxxxxxxxx',
caller: 'xxxxxxx',
sourceIp: 'test-invoke-source-ip',
accessKey: 'xxxxxxx',
cognitoAuthenticationProvider: null,
user: 'AIDAQxxxxxEBGY'
},
domainName: 'testPrefix.testDomainName',
apiId: 'y2o41dcif7'
},
body: null,
isBase64Encoded: false
}
restapi非代理集成lambda
创建名为GetStartedLambdaIntegration
的函数
- 相比代理集成,非代理集成的lambda只从集成请求体中获取输入。函数可以返回任何 JSON 对象、字符串、数字、布尔值甚至二进制 blob 的输出
- 代理集成的 Lambda 函数可以从任何请求数据获取输入,但必须返回特定 JSON 对象
- 对于自定义集成需要创建映射模板完成前后端数据的转换
这里官方文档给出的是老版本lambda的写法。。。很离谱
'use strict';
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var times = ['morning', 'afternoon', 'evening', 'night', 'day'];
exports.handler = async (event)=> {
console.log(event);
// Parse the input for the name, city, time and day property values
let name = event.name === undefined ? 'you' : event.name;
let city = event.city === undefined ? 'World' : event.city;
let time = event.time ? 'morning' : event.time;
let day = days.indexOf(event.day)<0 ? null : event.day;
// Generate a greeting
let greeting = 'Good ' + time + ', ' + name + ' of ' + city + '. ';
if (day) greeting += 'Happy ' + day + '!';
// Log the greeting to CloudWatch
console.log('Hello: ', greeting);
let response = {
"greeting": greeting
};
// Return a greeting to the caller
return response;
};
创建restapi非代理集成lambda
-
步骤和代理集成一致,但是需要开启cors预检
-
创建方法时,不要勾选lambda代理集成
-
设置2个必须参数,time为查询参数,day为请求头参数
创建如下model完成请求体数据映射
-
schema是固定写法
-
内容类型为application/json
我们已经知道非代理集成只能从集成请求体中获取参数,因此需要对方法请求创建model映射
然后在集成请求中对参数进行解析
- time和day来自于请求参数
- callername来自于根路径
总结一下
- model定义了客户端请求规范
- mapping定义了如何从方法请求构造集成请求
在控制台测试请求
我们看看lambda函数的事件输入
- 看起来很简单实际上是apigateway帮我们处理了
- 优点是lambda函数不需要承担解析任务
- 缺点是每次修改数据模型都需要重新部署apigateway
{
city: 'Seattle',
time: 'morning',
day: 'Wednesday',
name: 'John'
}
在终端进行请求
$ awscurl --service execute-api -X POST --region cn-north-1 "https://qzoqsj2lp4.execute-api.cn-north-1.amazonaws.com.cn/test/city?name=John&city=Seattle"
{"greeting":"Good , of Seattle. "}