-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redirect preserve query string #11
Comments
@joknoxy do we need to handle the case where the query string has multi values? function objectToQueryString(obj) {
var str = [];
for (var param in obj)
if (obj[param].multiValue)
str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param].multiValue.map((item) => item.value).join(',')))
else if (obj[param].value == '')
str.push(encodeURIComponent(param));
else
str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param].value));
return str.join("&");
} While checking further, I noticed an issue with this approach. If the query string is already an encoded string, we will be encoding it again in the cloudfront function. So I end up using this: function objectToQueryString(obj) {
var str = [];
for (var param in obj)
if (obj[param].multiValue)
str.push(param + "=" + obj[param].multiValue.map((item) => item.value).join(','));
else if (obj[param].value == '')
str.push(param);
else
str.push(param + "=" + obj[param].value);
return str.join("&");
} |
Thank you @akarsh-k, this works perfectly with the CloudFront event example structure. |
We ended up using a slight modification that preserves original grouping of multiValues and has some documentation: /**
* Patches lack of
* https://developer.mozilla.org/en-US/docs/Web/API/Location/search in event.
* Inspired by
* https://github.com/aws-samples/amazon-cloudfront-functions/issues/11.
* @param obj The weird format exposed by CloudFront
* https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-event-structure.html#functions-event-structure-query-header-cookie
* @returns {string} Tries to return the same as
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
*/
function getURLSearchParamsString(obj) {
var str = [];
for (var param in obj) {
if (obj[param].multiValue) {
str.push(
obj[param].multiValue.map((item) => param + "=" + item.value).join("&")
);
} else if (obj[param].value === "") {
str.push(param);
} else {
str.push(param + "=" + obj[param].value);
}
}
return str.join("&");
} Now |
Adapted JSDoc types from https://www.npmjs.com/package/@types/aws-lambda /**
* Patches lack of
* https://developer.mozilla.org/en-US/docs/Web/API/Location/search in event.
* Inspired by
* https://github.com/aws-samples/amazon-cloudfront-functions/issues/11.
* @param {import("aws-lambda"). CloudFrontFunctionsQuerystring} querystring The weird format exposed by CloudFront
* https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-event-structure.html#functions-event-structure-query-header-cookie
* @returns {string} Tries to return the same as
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
*/
function getURLSearchParamsString(querystring) {
var str = [];
for (var param in querystring) {
var query = querystring[param];
var multiValue = query.multiValue;
if (multiValue) {
str.push(multiValue.map((item) => param + '=' + item.value).join('&'));
} else if (query.value === '') {
str.push(param);
} else {
str.push(param + '=' + query.value);
}
}
return str.join('&');
} |
@longzheng Instead of |
Sorry I made a typo when I edited it, it can also be |
It's especially confusing that AWS offers a |
okay so I updated this a bit on my end because I was worried about double encoding, but also worried about not having encodings properly in query params. I'm also not redirecting here like in the example above. this was super helpful though, thank you @joknoxy ! function isEncoded(uri) {
uri = uri || '';
return uri !== decodeURIComponent(uri);
}
function fullyDecodeURI(uri){
while (isEncoded(uri)) {
uri = decodeURIComponent(uri);
}
return uri;
}
function encode(param) {
return encodeURIComponent(fullyDecodeURI(param));
}
function objectToQueryString(obj) {
var str = [];
for (var param in obj) {
if (obj[param].multiValue) {
str.push(encode(param) + "=" + obj[param].multiValue.map((item) => encode(item.value)).join(','));
} else if (obj[param].value == '') {
str.push(encode(param));
} else {
str.push(encode(param) + "=" + encode(obj[param].value));
}
}
return str.join("&");
}
function handler(event) {
var request = event.request;
request.headers["x-forwarded-host"] = request.headers.host;
request.querystring = objectToQueryString(request.querystring);
return request;
} |
Thanks for the comments, it helps me to solve my issue. Here is my code for https://gist.github.com/karpolan/ecce9c372bebb448ee04cc240ca5c8aa |
thanks @longzheng for sharing #11 (comment), got this working thanks to it 🚀 for anyone else trying to build function handler(event) {
// Get the request object from the event
var request = event.request;
// Get the URI of the requested resource
var uri = request.uri;
// Get the query string parameters
var queryStringParameters = request.querystring;
// Remove all trailing slashes from the URI using a regular expression
var newUri = uri.replace(/\/+$/, "");
// Check if the URI had trailing slashes
if (newUri !== uri) {
// Check if querystring is not empty
var hasQueryString = Object.keys(queryStringParameters).length > 0;
// Construct the new URI without the trailing slashes and include the query parameters if querystring is not empty
var redirectUri = newUri + (hasQueryString ? '?' + getURLSearchParamsString(queryStringParameters) : '');
// Redirect to the new URI without the trailing slashes
var response = {
statusCode: 301,
statusDescription: "Moved Permanently",
headers: {
location: { value: redirectUri },
},
};
return response;
}
// If there's no trailing slash, proceed with the request as is
return request;
}
// Helper function to format query string parameters
function getURLSearchParamsString(querystring) {
var str = [];
for (var param in querystring) {
var query = querystring[param];
var multiValue = query.multiValue;
if (multiValue) {
str.push(multiValue.map((item) => param + '=' + item.value).join('&'));
} else if (query.value === '') {
str.push(param);
} else {
str.push(param + '=' + query.value);
}
}
return str.join('&');
} |
This saved me! I kept getting Is this issue still open?! |
This relates to issue #7 . I've used the code from there and made it more generic and fixed a couple of edge-cases that wouldn't have worked. Please add this to sample library as it's a very common use-case with a non-obvious solution (for those of us not js experts).
The text was updated successfully, but these errors were encountered: