Redirect to Function - Splat is not working as expected

Thanks will test that out and report back if this works for me.

Seems like an inaccuracy in either the implementation or the documentation about it though that cost me quite some time :confused:

Yeah :confused: it may be one of those fringe cases that operates a bit tricky. I happen to be doing dev on a project that has a test function. Let me try that out live real quick and make sure my prior notes are correct :+1:t2:

Yes, that indeed works for me:

https://netlify-redirect-foo.netlify.app/lala outputs lala now with this code:

const file = event.path.substring(1)
1 Like

Alright, so I ran some test cases and did indeed find some interesting results.

Setup

Given the following _redirects record for a splat-injected rewrite:

/test/* /.netlify/functions/test-rewrite-to-function?parm=:splat 200

I ran the following tests (using my favorite CLI tool, httpie:

Standard:

http https://sun.sargesites.com/test/hello

Which yielded the following object for the event argument to the function (I removed some of the headers for brevity; unimportant to test):

{
  "path": "/test/hello",
  "httpMethod": "GET",
  "headers": {
    "accept": "*/*",
    "accept-encoding": "gzip",
    "connection": "keep-alive",
    "user-agent": "HTTPie/2.1.0",
    "via": "https/1.1 Netlify[840aa3b4-795a-4b07-aa09-e6f488912ae4] (ApacheTrafficServer/7.1.11)",
    "x-cdn-domain": "global.netlify.com",
    "x-country": "US",
    "x-forwarded-proto": "https",
  },
  "queryStringParameters": {},
  "multiValueQueryStringParameters": {},
  "body": "",
  "isBase64Encoded": true
}

So there was actually no query string data when the Function was called, but the initial path is reported. :thinking: Okay moving forward.

Call direct with parms:

http https://jonsdomain.com/.netlify/functions/test-rewrite-to-function?parm=hello

Which yielded the following object for the event argument to the function (I removed some of the headers for brevity; unimportant to test):

{
  "path": "/.netlify/functions/test-rewrite-to-function",
  "httpMethod": "GET",
  "headers": {
    "accept": "*/*",
    "accept-encoding": "gzip",
    "user-agent": "HTTPie/2.1.0",
    "via": "https/1.1 Netlify[840aa3b4-795a-4b07-aa09-e6f488912ae4] (ApacheTrafficServer/7.1.11)",
    "x-country": "US",
    "x-forwarded-proto": "https",
  },
  "queryStringParameters": {
    "parm": "hello"
  },
  "multiValueQueryStringParameters": {
    "parm": [
      "hello"
    ]
  },
  "body": "",
  "isBase64Encoded": true
}

So the path matches the origin request and the query string parameter was parsed into event.queryStringParameters :thinking: so the standard Lambda system works! That’s good, at least :stuck_out_tongue:

Call direct with empty param:

http https://jonsdomain.com/.netlify/functions/test-rewrite-to-function?parm=

Which yielded the following object for the event argument to the function (I removed some of the headers for brevity; unimportant to test):

{
  "path": "/.netlify/functions/test-rewrite-to-function",
  "httpMethod": "GET",
  "headers": {
    "accept": "*/*",
    "accept-encoding": "gzip",
    "connection": "keep-alive",
    "user-agent": "HTTPie/2.1.0",
    "via": "https/1.1 Netlify[c9c5b509-8a45-4a7c-a4b5-03f81d19d15f] (ApacheTrafficServer/7.1.11)",
    "x-cdn-domain": "global.netlify.com",
    "x-country": "US",
    "x-forwarded-proto": "https",
  },
  "queryStringParameters": {
    "parm": ""
  },
  "multiValueQueryStringParameters": {
    "parm": [
      ""
    ]
  },
  "body": "",
  "isBase64Encoded": true
}

Just wanted to run this test to see how the function input is given when the query string parameter is present but empty. This is important, but the key takeaway here is that the event.queryStringParameters does have the parm in it, even though it’s empty.


Conclusions:

  1. I think the path parameter does accurately reflect the path specified in the origin request, both for standard Function calls and Function calls via Rewrite proxy
  2. For whatever reason, the 200-based Rewrite operation ignores query parameters altogether. At first I thought it just wasn’t hydrating the :splat but as test #3 shows, even if the :splat was empty, the ?parm= part would still show in event.queryStringParameters – this indicates to me that the Rewrite engine is actually dropping the the query-string component altogether (the ? and everything after), which seems odd. @Dennis or @Scott do either of you have knowledge on why that could be?
1 Like

Unfortunately, while it does look like the redirect should be parsed correctly from the netlify-redirect-parser source code, the netlify-redirector (the engine that actually executes the redirects) is one of Netlify’s few closed-source projects. Wasm is real :sweat_smile: Hopefully the official team can chime in on this :+1:t2:


Jon

1 Like

Hey y’all!

So, I’m pretty sure I’ve had a very similar discussion not too long ago. It’s not the best news but hopefully it’ll give you some answers!

Nice! Thanks for the follow up @Scott! I probably should’ve searched before doing my own digging Community :man_facepalming:t2: but glad to know it’s known and confirmed. Appreciate it! I ended up doing similar - just parsing the path in the Function :grin:


Jon

1 Like

I actually read that, but was not sure if it was similar to my problem as it kept talking about query parameters - which I don’t use. But maybe I did not get the gist of the thread at all, rereading it now with what I do know now it seems quite relevant indeed.

Still a bug/limitation in my opinion that should possibly be mentioned in the docs :wink:

I am not sure if this is limited to Rewrites to functions by the way or applies also to rewrites to external URLs or other internal URLs that are not functions. I would expect the same problem to be present there really.

Until this bug (?) gets fixed, you can try the following workaround:

1 Like

thanks for sharing that, @brun0! We’re hoping to have an update on this soon. Its on our radar.

Netlify splat rewrites work with paths but not with query parameters. I ran into this problem myself. It’s documented if you look hard enough, but it’s not immediately clear. One way to work around this is to use the Javascript function btoa to Base64 encode your parameters on the client side and then use atob to decode them from the path on the server size. So, if you want example.com/test?a=1&b=2, you’d instead request example.com/test/YT0xJmI9Mg== and your serverless function can turn that back into a=1&b=2 by decoding it.

Haha that’s clever! Very nice :grin:

Coming back to this one to provide some code for those in the future.

Tl;dr:

Netlify doesn’t support redirects that take the content of a * and inject it as a query-string parameter with :splat a la attempting:

/admin/* /login?admin-path=:splat 302!`

Or anything of the sort. It’s just not supported right now.


Work-Around:

If you want that functionality, just add a new Function with the following code (using your targeted URL / query param):

// jons-splat-redirector.js

exports.handler = async (event) => {
  pathBack = encodeURIComponent(event.path)

  return {
    statusCode: 302,
    headers: {
      Location: `/login?admin-path=${pathBack}`
    }
  }
}

and use the following _redirects record to hit your Function without the user ever seeing it :+1:t2:

/admin-page/* /.netlify/functions/reauthorize 200!

Essentially instead of using :splat the Function will just read the incoming path, whatever it is, and shim it into the query string you need then send back the correct 302. YMMV :slight_smile:


Jon

Once this update has ironed out the bug and live, I am assuming it would solve this problem? Just double checking :slight_smile:

1 Like

I don’t believe so; that update doesn’t speak at all to star-to-splat changes / doesn’t hint that functionality there might change. I believe this feature req. still stands :+1:t2:

General update here: Until recently the suggestion from Redirect to Function - Splat is not working as expected - #2 by jonsully worked fine for me and the variable was filled as described. Recently that started to not be the case any more on some function runs, instead the string is just empty. No idea what changed on Netlify side :man_shrugging:

Fortunately I could get rid of the param in my limited use case that was still live. Still confusing.

@jonsully, I think this issue is related to the fact that query param values should probably have any slashes escaped. So your conclusion correct that if * is a path (which usually contains slashes) it will not work as a splat value of a query param.

That said, I think we introduced a change to our URL parser just now that might help resolve this issue. Could one of y’all with a test case check if the behavior has changed?

Hey @Dennis! I finally got a few minutes to play with some of this again.

Dec 2020 Update:

1) - The desired sort of redirect (taking a path and making it a URL string parm on the other side) still does not work as we’d think:

images/* /.netlify/functions/image-processor?image=:splat 200

On the incoming function, the event.queryStringParameters === {} so for now, I continue to believe that the above-mentioned workaround remains the best option.

2) - @brun0’s trick above does appear to work, but forces all calls to the production function, which is a roadblock for local development.

3) - @janpio’s note is concerning, although I haven’t noticed this myself and my thats.at project continues to work generally fine leveraging the workaround noted above.

@Dennis - per this thought, I’d read through some of the source behind the netlify-redirect-parser a few months back and I did leave believing that the * section of the URL was being understood and contained correctly, but my presumption was that the netlify-redirector itself (closed source) wasn’t satisfied with that value or perhaps has some syntax restrictions around a rewrite that leverages a query string parameter. Not sure.

To point out, the correct and working way to handle paths to a function, as mentioned by @jonsully, is to use the path by doing this:

/images/* /.netlify/functions/image-processor/:splat 200

The path can be accessed using event.path inside the function.

Also, @jonsully, I think the issue with using path as querystring param values is that it can have a slash in it. And if unescaped, the slash would make the whole querystring invalid.

That said, your first guess that the issue is with the value is what I have found to be the cause. And a related bugfix that was rollbacked should address this issue when redeployed. I don’t know when that will be yet but I recommend keeping an eye on that if you want to use paths as querystring param values.