Fails with `/` route
Closed this issue · 8 comments
Python Version
3.9.6
Package Version
2.12.1
Description
Summary
When the API Gateway has a path of simply /
, the library returns:
[ERROR] KeyError: 'path'
Traceback (most recent call last):
File "/var/lang/lib/python3.9/site-packages/apig_wsgi/__init__.py", line 73, in handler
environ = get_environ_v1(
File "/var/lang/lib/python3.9/site-packages/apig_wsgi/__init__.py", line 107, in get_environ_v1
"PATH_INFO": urllib.parse.unquote(event["path"], encoding="iso-8859-1"),
This is related to, but not the same as #292, which was trivially solved using a wildcard route ie /{proxy+}
. However this bug concerns the case where you specifically need the root route to work.
Replication
Unfortunately I don't have any CloudFormation code just for the moment, but I can describe my resources:
$ aws apigateway get-resources --rest-api-id <my-api-id>
{
"items": [
{
"id": "ccgexbzsj1",
"path": "/",
"resourceMethods": {
"ANY": {}
}
}
]
}
Interestingly, even when I remove the root resource, and add a wildcard route, this bug still happens if I hit ignore this, the proxy was incorrectly configured here. With this config, hitting /
:/
simply fails to find a handler and you get {"message":"Missing Authentication Token"}
:
{
"items": [
{
"id": "893apb",
"parentId": "ccgexbzsj1",
"pathPart": "{proxy+}",
"path": "/{proxy+}",
"resourceMethods": {
"ANY": {}
}
},
{
"id": "ccgexbzsj1",
"path": "/"
}
]
}
In case this matters, here are some details about my API gateway:
$ aws apigateway get-rest-api --rest-api-id nrfudu8zu0
{
"id": "nrfudu8zu0",
"name": "SOME_NAME",
"createdDate": "2021-09-30T14:39:29+10:00",
"version": "1.0",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"EDGE"
]
},
"tags": { },
"disableExecuteApiEndpoint": false
}
Are you using the "proxy" integration type for your root path? Because it's possible integrate lambda with the "custom" integration type as well.
The example app has a root handler that works fine:
apig-wsgi/example/deployment/files/cloudformation_site.yml
Lines 91 to 101 in 0373f88
If you could get a dump of the event for a root request, that would also help debug.
Are you using the "proxy" integration type for your root path? Because it's possible integrate lambda with the "custom" integration type as well.
Yes:
$ aws apigateway get-integration --rest-api-id nrfudu8zu0 --resource-id t1kwx0 --http-method ANY
{
"type": "AWS_PROXY",
"httpMethod": "POST",
"uri": "MY_GATEWAY/functions/MY_LAMBDA/invocations",
"passthroughBehavior": "WHEN_NO_MATCH",
"contentHandling": "CONVERT_TO_TEXT",
"timeoutInMillis": 29000,
"cacheNamespace": "t1kwx0",
"cacheKeyParameters": [
"method.request.path.proxy"
],
"integrationResponses": {
"200": {
"statusCode": "200",
"responseTemplates": {
"application/json": null
}
}
}
}
The example app has a root handler that works fine:
I note that you actually have two different methods here, RestApiRootANY
which uses the root URL (via ResourceId: !GetAtt RestApi.RootResourceId
), and RestApiProxyANY
which uses the '{proxy+}'
wildcard via RestApiProxy
. This isn't what I had, but actually even if I do something like it I still get the above error.
If you could get a dump of the event for a root request, that would also help debug.
So I setup a lambda handler that just prints out the event
variable and then passes it to an apig_wsgi
handler:
_webapp_handler = make_lambda_handler(app)
def webapp_handler(event, context):
print(json.dumps(event, indent=2, sort_keys=True))
response = _webapp_handler(event, context)
print(json.dumps(response, indent=2, sort_keys=True))
return response
Then, when I hit /
, it prints out {}
, ie an empty dictionary, immediately before throwing [ERROR] KeyError: 'path'
:
START RequestId: 2cd51040-811a-4467-9cba-8b85e99ed3bb Version: $LATEST
--
{}
[ERROR] KeyError: 'path'Traceback (most recent call last): File "/var/task/hyddb2/handler.py", line 22, in webapp_handler response = _webapp_handler(event, context) File "/var/lang/lib/python3.9/site-packages/apig_wsgi/__init__.py", line 73, in handler environ = get_environ_v1( File "/var/lang/lib/python3.9/site-packages/apig_wsgi/__init__.py", line 107, in get_environ_v1 "PATH_INFO": urllib.parse.unquote(event["path"], encoding="iso-8859-1"),
END RequestId: 2cd51040-811a-4467-9cba-8b85e99ed3bb
Okay, there's no sensible way for apig-wsgi to handle an empty dictionary. There's something wrong in your setup and I don't think I can really help any further. I can handle bug reports but not free AWS support 😅
You can ask AWS Support why you're getting an empty dict.
I'd laso suggest you just try rebuilding your API from scratch, preferably using the new HTTP API type which has a lot less configuration:
apig-wsgi/example/deployment/files/cloudformation_site.yml
Lines 65 to 80 in 0373f88
If you do figure out in what way your existing API is wrong then maybe we can add a note in the docs warning against the particular misconfiguration.
Oh, I wasn't asking for AWS support, I thought the obvious implication of my bug was that we should treat the absence of a path as being the root URL. In other words, you do event.get("path", "/")
instead of event["path"]
. Surely I have proven to you that the API Gateway works in this way. I'm actually using AWS SAM which does some magic abstraction on top of the Gateway, which is why I've been posting the resource descriptions and not the CloudFormation template (because it doesn't exist). But nonetheless I think this is simply what happens when you hit the root route and you haven't specifically set up a root resource using !GetAtt RestApi.RootResourceId
as you have done in your example.
And the motivation for this, by the way, is that AWS SAM doesn't offer an easy way to get the root resource, because we don't actually create a RestApi
resource at all, it's just done automagically by SAM. So it would be a small workaround for this use case.
I thought the obvious implication of my bug was that we should treat the absence of a path as being the root URL. In other words, you do
event.get("path", "/")
instead ofevent["path"]
I don't think that is implied at all. You are receiving an empty dictionary - no data. If we receive actual event data, there's always a path. The event format is specified here, and path is not optional: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
Also there's no way of coming up with defaults for the rest of the required WSGI variables.
I'm actually using AWS SAM which does some magic abstraction on top of the Gateway, which is why I've been posting the resource descriptions and not the CloudFormation template (because it doesn't exist).
Afaiu SAM is a layer on top of CloudFormation: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy.html
If you visit the CloudFormation console, do you not see CloudFormation stacks?
Anyway if the template generated by SAM doesn't correctly set up your root resource, check with the SAM docs / team / AWS Support.
Yeah, good call. I missed the significance of the whole event
dict being empty.
SAM absolutely is a layer on top of CFN, I'm just making the point that solving this problem isn't trivial when using SAM because you don't directly have access to the API Gateway resource, unless you construct it all yourself, at which point you're not really taking advantage of SAM.
I will contact AWS and see what they say.