Is it possible to use wildcards or catch-all paths in AWS API Gateway - aws-api-gateway

I am trying to redirect all traffic for one domain to another. Rather than running a server specifically for this job I was trying to use AWS API Gateway with lambda to perform the redirect.
I have this working ok for the root path "/" but any requests for sub-paths e.g. /a are not handled. Is there a way to define a "catch all" resource or wildcard path handler?

As of last week, API Gateway now supports what they call “Catch-all Path Variables”.
Full details and a walk-through here: API Gateway Update – New Features Simplify API Development

You can create a resource with path like /{thepath+}. Plus sign is important.
Then in your lambda function you can access the value with both
event.path - always contains the full path
or event.pathParameters.thepath - contains the part defined by you. Other possible use case: define resource like /images/{imagepath+} to only match pathes with certain prefix. The variable will contain only the subpath.
You can debug all the values passed to your function with: JSON.stringify(event)
Full documentation

Update: As of last week, API Gateway now supports what they call “Catch-all Path Variables”. See API Gateway Update – New Features Simplify API Development.
You will need to create a resource for each level unfortunately. The reason for this is API Gateway allows you to access those params via an object.
For example: method.request.path.XXXX
So if you did just /{param} you could access that with: method.request.path.param but if you had a nested path (params with slashes), it wouldn't work. You'd also get a 404 for the entire request.
If method.request.path.param was an array instead...then it could get params by position when not named. For example method.request.path.param[] ...Named params could even be handled under there, but accessing them wouldn't really be easy. It would require using something some sort of JSON path mapping (think like what you can do with their mapping templates). Sadly this is not how it's handled in API Gateway.
I think it's ok though because this might make configuring API Gateway even more complex. However, it does also limit API Gateway and to handle this situation you will ultimately end up with a more confusing configuration anyway.
So, you can go the long way here. Create the same method for multiple resources and do something like: /{1}/{2}/{3}/{4}/{5}/{6}/{7} and so on. Then you can handle each path parameter level if need be.
IF the number of parameters is always the same, then you're a bit luckier and only need to set up a bunch of resources, but one method at the end.
source: https://forums.aws.amazon.com/thread.jspa?messageID=689700&#689700

Related to HTTPAPI that AWS introduced recently, $default is used a wildcard for catching all routes that don't match a defined pattern.
For more details, refer to: aws blogs

You can create a resource with path variable /{param}, and you can treat this as wildcard path handler.
Thanks,
- Ka Hou

Related

Redirect S3 subfolder to another domain with Cloudfront

I have a static showcase website hosted on S3 and using CloudFront, and an online shop (Prestashop) and a blog (Wordpress), both hosted on OVH servers.
I want to make a hidden redirection on two subfolders of my static website so it acts like my 3 websites are on the same host, using the following pattern :
mysite.com/ --> normal behaviour
mysite.com/blog/ --> myblog.com/
mysite.com/store/ --> mystore.com/
Of course, I need every request to be handled that way, eventually having something like that :
mysite.com/store/fr/1-myproduct.html
returns what
mystore.com/fr/1-myproduct.html
would have returned.
This seems really tricky, since I've found no real solution to my problem, and at this point I doubt it may even be possible to do such a thing.
I considered using a proxy but wouldn't that be like using a sledgehammer to get rid of a fly ?
I have searched for any possible redirection and I was only able to find subdomain/domain redirections...
So my question would be "How can I do that ?"
But right now I'm wondering "Can one do that ?"
P.S : It's my first post ever, I'm used to search for a long time before posting and I always end up finding a solution, except for now. Any suggestion is welcome.
I'll check about proxies since it's my last hope
Wait.
I have a static showcase website hosted on S3 and using CloudFront
CloudFront is a reverse proxy.
Depending on how much flexibility you have with the other two sites, CloudFront can potentially take you where you want to go, combining multiple independent sites under one hostname.
This is done by creating additional origin servers for your distributions and then creating additional cache behaviors, with path patterns matching the additonal paths, such as /blog and /blog/* that send requests to the alternate origins.
There is, however, a catch. CloudFront can't remove the matched pattern, so mainsite.example.com/blog/hello-world, matching the pattern /blog/* will be forwarded to blog.example.com/blog/hello-world -- not to blog.example.com/hello-world.¹ This will require changes to the other sites in order to integrate them in this way.
Unless...
If you already have unique path patterns, no problem, but if the extra sites' content is in the root of each individual site, you see the issue, here. Not insurmoubtable, but still an issue.
Your only alternative will be a reverse proxy behind CloudFront to rewrite those paths and send the requests on to the alternate servers. Truly not a big deal either, since HAProxy, Nginx, and Varnish all offer such functionality and can handle a large number of proxied requests on surprisingly small hardware.
The recently (2017) released Lambda#Edge service allows you to rewrite paths on the fly, as requests are processed, if necessary.
But the bottom line is that the reason you have not found a real solution other than a proxy is that there is no alternative -- every path at a given hostname must be handled in one logical place -- one group of one or more identically-configured endpoints. In the case of CloudFront, the logical place is physically distributed globally.
¹ CloudFront, natively, can actually prepend onto the path before forwarding the request, so requests for mainsite.example.com/bar/fizz can be forwarded to foosite.example.com/foo/bar/fizz by setting the origin path to /foo when you configure the origin. But it can't remove path parts or otherwise modify the path without also using Lambda#Edge. In the scenario discussed above, you would leave the origin path blank when configuring the additional origin servers.
Single S3 bucket with the following behavior :
domain.com-> serves the files from root of bucket
domain.com/blog -> serves the files from subfolder in S3 bucket (this is not default behavior)
How to :
https://aws.amazon.com/ru/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/
Lambda edge code:
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/\/$/, '\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri);
console.log("New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
// Return to CloudFront
return callback(null, request);
};
Summary of code higher :
lambda edge rewrites the path "/blog/" to "/blog/index.html"

How to get the original request URI in api gateway?

From a lambda implemented api gateway resource, how to get the original request URI. Or even just the original path?
Lacking a better way I'm currently using the following three variables that I pass down to the lambda using the default request template:
$context.resourcePath contains the path with variable names ex: "/blah/{var}"
$input.params().path contains the variable names and values ex: {"var":"something"}
$context.stage contains the stage ex: "prod"
That's quite a hassle since it requires path variable substitution to get the original call path:
/prod/blah/something
How can I get the original URL or URI?
I'm not finding anything in the documentation that lets you get the original call URI. I can add a feature request to consider adding it. Can you describe your use case. Why do you want to get the original URI?
I found a 'workaround'.
If you create a custom domain name with a BasePathMapping, and call the API using this custom domain, the original request uri actually has your stage name in there:
Call directly to the API gateway:
curl -v 'https://some-id.execute-api.eu-central-1.amazonaws.com/v1/ping'
...
request.url: https://some-id.execute-api.eu-central-1.amazonaws.com/ping'
But if we call it through te custom domain (which is actually a cloudfront distribution):
curl -v -X GET https://api.our.domain.name.com/v1/ping
...
request.url: https://api.our.domain.name.com/v1/ping
In my opinion, the direct call gives you an INCORRECT request url in the lambda function, as the url very clearly has the stage name in there.
This breaks the routing middleware of at least flask.
Any update on the feature request?

how call REST service with path variable in webmethod?

I'm using WM9.8. I want to know how to call a GET REST service with path variable like:
http://localhost:8080/client/1 in webmethod.
I can call POST rest service using pub.client.http. But it dosen't work to GET.
Use String varible called "method" to set type of Http request method.
Just put the path variable in the URL and made a substitution to the path variable
e.g: http://localhost:8080/client/%yourPathVariableHere%
Holy cow this is an old question but I just tumbled across it and I thought I might helps somebody else who does.
URLs in webmethods are fixed to a single value, like /client unless you enable watt.server.url.alias.partialMatching=true
After that, you can simply alias a service to /client and all subURLs like /client/1 are sent to that service. You still have to parse them to get the ID out.
Be careful, though, because ALL sub URLs are sent to the service. So after enabling this flag I get /client, /client/1, /client/1/name all going to the same service. You can see how this can quickly become REST-unfriendly.

Is it bad practice to allow specifying parameters in URL for POST

Should parameters for POST requests (elements of the resource being created) be allowed to be added to the URL as well as in the body?
For example, let say I have a POST to create a new user at
/user
With the full set of parameters name, email, etc... in the body of the request.
However, I've seen many API's would accept the values in either the body or URL parameters like this:
/user?name=foo&email=foo#bar.com
Is there any reason this second option, allowing the parameters in the URL is bad practice? Does it violate any component of REST?
The intent of a query parameter is to help identify the target resource for a request. The body of a POST should be used to specify instructions to the server.
The query component contains non-hierarchical data that, along with
data in the path component (Section 3.3), serves to identify a
resource within the scope of the URI's scheme and naming authority
(if any).
    -- RFC 3986 Section 3.4
The hierarchical path component and optional query component serve
as an identifier for a potential target resource within that origin
server's name space.
    -- RFC 7230 Section 2.7.1
The Udacity Web Development course, be Steve Huffman (the man behind Reddit), recommends only using POST requests to update server side data. Steve highlights why using GET parameters to do so can be problematic.

RESTful, efficient way to query List.contains(element)?

Given:
/images: list of all images
/images/{imageId}: specific image
/feed/{feedId}: potentially huge list of some images (not all of them)
How would you query if a particular feed contains a particular image without downloading the full list? Put another way, how would you check whether a resource state contains a component without downloading the entire state? The first thought that comes to mind is:
Alias /images/{imageId} to /feed/{feedId}/images/{imageId}
Clients would then issue HTTP GET against /feed/{feedId}/images/{id} to check for its existence. The downside I see with this approach is that it forces me to hard-code logic into the client for breaking down an image URI to its proprietary id, something that REST frowns upon. Ideally I should be using the opaque image URI. Another option is:
Issue HTTP GET against /feed/{feedId}?contains={imageURI} to check for existence
but that feels a lot closer to RPC than I'd like. Any ideas?
What's wrong with this?
HEAD /images/id
It's unclear what "feed" means, but assuming it contains resources, it'd be the same:
HEAD /feed/id
It's tricky to say without seeing some examples to provide context.
But you could just have clients call HEAD /feed/images/{imageURI} (assuming that you might need to encode the imageURI). The server would respond with the usual HEAD response, or with a 404 error if the resource doesn't exist. You'd need to code some logic on the server to understand the imageURI.
Then the client either uses the image meta info in the head, or gracefully handles the 404 error and does something else (depending on the application I guess)
There's nothing "un-RESTful" about:
/feed/{feedId}?contains={imageURI}[,{imageURI}]
It returns the subset as specified. The resource, /feed/{feedid}, is a list resource containing a list of images. How is the resource returned with the contains query any different?
The URI is unique, and returns the appropriate state from the application. Can't say anything about the caching semantics of the request, but they're identical to whatever the caching semantics are of the original /feed/{feedid}, it simply a subset.
Finally, there's nothing that says that there even exists a /feed/{feedid}/image/{imageURL}. If you want to work with the sub-resources at that level, then fine, but you're not required to. The list coming back will likely just be a list of direct image URLS, so where's the link describing the /feed/{feedid}/image/{imageURL} relationship? You were going to embed that in the payload, correct?
How about setting up a ImageQuery resource:
# Create a new query from form data where you could constrain results for a given feed.
# May or may not redirect to /image_queries/query_id.
POST /image_queries/
# Optional - view query results containing URIs to query resources.
GET /image_queries/query_id
This video demonstrates the idea using Rails.