Code Monkey home page Code Monkey logo

Comments (11)

edzis avatar edzis commented on September 16, 2024 12

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
?aa=11&bb=22&aa=33&bb=44,55&cc=66&dd&ee
results in
?aa=11&aa=33&bb=22&bb=44,55&cc=66&dd&ee
instead of
?aa=11,33&bb=22,44,55&cc=66&dd&ee

from amazon-cloudfront-functions.

akarsh-k avatar akarsh-k commented on September 16, 2024 9

@joknoxy do we need to handle the case where the query string has multi values?
I have modified objectToQueryString as shown below.

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("&");
}

from amazon-cloudfront-functions.

longzheng avatar longzheng commented on September 16, 2024 7

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('&');
}

from amazon-cloudfront-functions.

cornwe19 avatar cornwe19 commented on September 16, 2024 2

It's especially confusing that AWS offers a querystring helper package which exposes a stringify method that doesn't actually work with their own representation of the event.request.querystring object https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-builtin-modules-query-string

from amazon-cloudfront-functions.

longzheng avatar longzheng commented on September 16, 2024 1

@longzheng Instead of CloudFrontFunctionsEvent I believe it should be CloudFrontFunctionsEvent['request']['querystring'].

Sorry I made a typo when I edited it, it can also be CloudFrontFunctionsQuerystring since that's what CloudFrontFunctionsEvent['request']['querystring'] references. I'll update my comment.

from amazon-cloudfront-functions.

Akshay090 avatar Akshay090 commented on September 16, 2024 1

thanks @longzheng for sharing #11 (comment), got this working thanks to it 🚀

for anyone else trying to build
cloudfront function to redirect to non trailing slash url while preserving query string
refer 👇

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('&');
}

from amazon-cloudfront-functions.

goetzc avatar goetzc commented on September 16, 2024

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:

Thank you @akarsh-k, this works perfectly with the CloudFront event example structure.

from amazon-cloudfront-functions.

edzis avatar edzis commented on September 16, 2024

@longzheng Instead of CloudFrontFunctionsEvent I believe it should be CloudFrontFunctionsEvent['request']['querystring'].

from amazon-cloudfront-functions.

lukevers avatar lukevers commented on September 16, 2024

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;
}

from amazon-cloudfront-functions.

karpolan avatar karpolan commented on September 16, 2024

Thanks for the comments, it helps me to solve my issue.

Here is my code for redirect with query params + adding missing /index.html for SPA or SSG websites

https://gist.github.com/karpolan/ecce9c372bebb448ee04cc240ca5c8aa

from amazon-cloudfront-functions.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.