如何让API Gateway验证HTTP查询字符串参数的取值规则?
我正尝试让API Gateway验证HTTP查询字符串参数的取值规则。通过配置绑定Content-Type为application/x-www-form-urlencoded的AWS::ApiGateway::Model资源,我将唯一的message参数限制为只能取foo或bar值。目前APIGW可以验证message参数是否存在:
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?messag=foo" {"message": "Missing required request parameters: [message]"}
但无法验证或限制参数的取值,例如传入whatever时仍能通过:
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?message=whatever" you sent 'whatever'
请问我哪里配置出错了?
AWSTemplateFormatVersion: '2010-09-09' Outputs: PublicApiEndpoint: Value: Fn::Sub: https://${PublicApiRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${PublicApiStage} Parameters: MemorySizeDefault: Default: '512' Type: String RuntimeVersion: Default: '3.8' Type: String TimeoutDefault: Default: '5' Type: String Resources: HelloFunction: Properties: Code: ZipFile: | def handler(event, context): message=event["queryStringParameters"]["message"] response="you sent '%s'" % message return {'statusCode': 200, 'headers': {"Content-Type": "text/plain"}, 'body': response} Handler: index.handler MemorySize: Ref: MemorySizeDefault Role: Fn::GetAtt: - HelloFunctionRole - Arn Runtime: Fn::Sub: python${RuntimeVersion} Timeout: Ref: TimeoutDefault Type: AWS::Lambda::Function HelloFunctionRole: Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Version: '2012-10-17' Policies: - PolicyDocument: Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: '*' Version: '2012-10-17' PolicyName: Fn::Sub: hello-function-role-policy-${AWS::StackName} Type: AWS::IAM::Role HelloEndpointMethod: Properties: AuthorizationType: NONE HttpMethod: GET Integration: IntegrationHttpMethod: POST Type: AWS_PROXY Uri: Fn::Sub: - arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${arn}/invocations - arn: Fn::GetAtt: - HelloFunction - Arn RequestParameters: "method.request.querystring.message": true RequestValidatorId: Ref: HelloEndpointValidator RequestModels: "application/x-www-form-urlencoded": HelloEndpointModel ResourceId: Ref: HelloEndpointResource RestApiId: Ref: PublicApiRestApi Type: AWS::ApiGateway::Method HelloEndpointPermission: Properties: Action: lambda:InvokeFunction FunctionName: Ref: HelloFunction Principal: apigateway.amazonaws.com SourceArn: Fn::Sub: arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PublicApiRestApi}/${PublicApiStage}/GET/hello Type: AWS::Lambda::Permission HelloEndpointResource: Properties: ParentId: Fn::GetAtt: - PublicApiRestApi - RootResourceId PathPart: hello RestApiId: Ref: PublicApiRestApi Type: AWS::ApiGateway::Resource HelloEndpointValidator: Properties: RestApiId: Ref: PublicApiRestApi ValidateRequestParameters: true Type: AWS::ApiGateway::RequestValidator HelloEndpointModel: Properties: RestApiId: Ref: PublicApiRestApi ContentType: 'application/x-www-form-urlencoded' Name: HelloEndpointModel Schema: "$schema": "http://json-schema.org/draft-04/schema#" type: object properties: message: type: string pattern: "^((foo)|(bar))$" required: - message Type: AWS::ApiGateway::Model PublicApiDeployment: DependsOn: - HelloEndpointMethod Properties: RestApiId: Ref: PublicApiRestApi Type: AWS::ApiGateway::Deployment PublicApiRestApi: Properties: Name: Fn::Sub: public-api-rest-api-${AWS::StackName} Type: AWS::ApiGateway::RestApi PublicApiStage: Properties: DeploymentId: Ref: PublicApiDeployment RestApiId: Ref: PublicApiRestApi StageName: 1-0-0 Type: AWS::ApiGateway::Stage
你的配置有两个核心问题:
RequestValidator仅校验参数存在性,不处理取值规则
你当前的HelloEndpointValidator只开启了ValidateRequestParameters: true,这个配置仅会检查RequestParameters中指定的参数是否存在,不会对参数的取值做校验——取值规则校验需要RequestModels配合ValidateRequestBody实现,但这里存在关键限制。GET查询字符串无法通过
application/x-www-form-urlencoded的Model校验
API Gateway中,RequestModels绑定的Content-Type仅作用于请求体(RequestBody),而GET请求的查询字符串不属于请求体范畴。你指定的application/x-www-form-urlencoded类型对应的是POST/PUT等请求的表单请求体,GET的查询参数不会被解析到这个Model对应的结构中,因此无法触发取值校验。
解决办法
要校验GET查询参数的取值,可选择以下两种方案:
方案一:使用Lambda授权器前置校验
在请求到达业务Lambda前,通过自定义授权器校验message的取值:
- 创建Lambda授权器函数,检查
event.queryStringParameters.message是否为foo或bar,不符合则返回拒绝策略 - 修改
HelloEndpointMethod的AuthorizationType为CUSTOM,并关联该授权器 - 确保授权器拥有API Gateway调用权限
方案二:在业务Lambda中直接校验
利用AWS_PROXY集成的透传特性,在业务逻辑开头加入校验:
def handler(event, context): message = event["queryStringParameters"]["message"] if message not in ["foo", "bar"]: return { 'statusCode': 400, 'headers': {"Content-Type": "application/json"}, 'body': '{"error": "message must be foo or bar"}' } response = "you sent '%s'" % message return {'statusCode': 200, 'headers': {"Content-Type": "text/plain"}, 'body': response}
补充说明
如果一定要用API Gateway原生Model校验,需将参数放到POST请求的表单体中,同时修改HelloEndpointMethod的HttpMethod为POST,开启ValidateRequestBody: true的RequestValidator,这样Model的pattern规则才会生效。
内容的提问来源于stack exchange,提问作者Justin

