fastify icon indicating copy to clipboard operation
fastify copied to clipboard

routerPath returns url with a trailing slash

Open ivan-tymoshenko opened this issue 2 years ago • 15 comments

Prerequisites

  • [X] I have written a descriptive issue title
  • [X] I have searched existing issues to ensure the issue has not already been raised

Issue

Originally this error comes from fastify-metrics plugin. It relies on two things:

  • onRoute hook to set up a metric for the route;
  • request routerPath property to find the route metric from the request context.

The problem happens when you use a plugin prefix with the prefixTrailingSlash option set to both (default) value. In this case, the onRoute hook will be called only once, so fastify-metrics will set up only one route, but internally there are two different routes with two different routerPath properties. So fastify-metrics will not record metrics for requests with trailing slash.

Example:

'use strict'

const fastify = require('fastify')()

fastify.addHook('onRoute', ({ method, url }) => {
 console.log('New route added:', method, url)
})

fastify.register(async (fastify) => {
 fastify.get('/', async (request) => {
   console.log(request.routerPath)
 })
}, { prefix: '/foo' })

fastify.ready(() => {
 fastify.inject({ method: 'GET', url: '/foo' })
 fastify.inject({ method: 'GET', url: '/foo/' })
})

Output:

New route added: GET /foo
New route added: HEAD /foo
New route added: HEAD /foo/
/foo
/foo/

If we don't call onRoute twice, that means that we want to hide the second route from a user. So from the user's perspective, there should be only one route. If it's true, then we should return routerPath with a trimmed trailing slash.

ivan-tymoshenko avatar Nov 15 '22 19:11 ivan-tymoshenko

Refs https://github.com/fastify/fastify/pull/2782

climba03003 avatar Nov 16 '22 06:11 climba03003

Maybe we should clone the options like https://github.com/fastify/fastify/pull/4273 for the /foo/ route.

climba03003 avatar Nov 16 '22 06:11 climba03003

If it's true, then we should return routerPath with a trimmed trailing slash.

Agreed!

mcollina avatar Nov 16 '22 11:11 mcollina

Hi @mcollina can I try to work on this isssue ? or someone has already take it ?

EliphazBouye avatar Feb 24 '23 15:02 EliphazBouye

Go for it!

mcollina avatar Feb 24 '23 15:02 mcollina

Can I get some more information about the expected result?

EliphazBouye avatar Feb 28 '23 11:02 EliphazBouye

The second request in this example should return/foo instead of /foo/. Expected output:

New route added: GET /foo
New route added: HEAD /foo
New route added: HEAD /foo/ <- not sure that we should call onRoute hook for this route
/foo
/foo <- without trailing slash

ivan-tymoshenko avatar Feb 28 '23 12:02 ivan-tymoshenko

Oh okay, I see and understand now thank you for enlightening me 👍🏾

EliphazBouye avatar Feb 28 '23 12:02 EliphazBouye

The second request in this example should return/foo instead of /foo/. Expected output:

New route added: GET /foo
New route added: HEAD /foo
New route added: HEAD /foo/ <- not sure that we should call onRoute hook for this route
/foo
/foo <- without trailing slash

you can fix it by using :

const fastify = require('fastify')({
  ignoreTrailingSlash: true
})

EliphazBouye avatar Feb 28 '23 15:02 EliphazBouye

  1. If one option works wrong, you can't fix it by setting another option. My example without ignoreTrailingSlash still would work wrong.
  2. If you add ignoreTrailingSlash = true it would trim trailing slashes from all routes, not only from bare prefixes.
'use strict'

const fastify = require('fastify')()

fastify.addHook('onRoute', ({ method, url }) => {
 console.log('New route added:', method, url)
})

fastify.register(async (fastify) => {
 fastify.get('/', async (request) => {
   console.log(request.routerPath)
 })
  fastify.get('/bar/', async (request) => {
   console.log(request.routerPath)
 })
}, { prefix: '/foo' })

fastify.ready(() => {
 fastify.inject({ method: 'GET', url: '/foo' })
 fastify.inject({ method: 'GET', url: '/foo/' })
 fastify.inject({ method: 'GET', url: '/foo/bar/' })
})

It shouldn't trim /foo/bar/, only /foo/, because it's a prefix.

ivan-tymoshenko avatar Mar 01 '23 09:03 ivan-tymoshenko

I've made some research on the code and made some debuging. but I ask if it's really an issue ? because when you give a URL to a prefix is not right to get the URL with the prefix behind? the routerPath return just the URL passed on the request

EliphazBouye avatar Mar 03 '23 14:03 EliphazBouye

The problem is inconsistency. Every time fastify registers a new route, the onRoute hook triggers. So I as a user can handle each new route. So it confuses me as a user when I see in the routerPath param the route that I haven't registered.

ivan-tymoshenko avatar Mar 29 '23 11:03 ivan-tymoshenko

the routerPath return just the URL passed on the request

no, it returns a route that was registered, not a URL string

ivan-tymoshenko avatar Mar 29 '23 11:03 ivan-tymoshenko

I am interested in contributing to this issue. Please let me know if there is anything else I should be mindful of.

yeeao avatar Jul 29 '23 03:07 yeeao

@ivan-tymoshenko I think the code is behaving as it is suppose to. If you would like to get rid of trailing slash from (/foo/) from being registered, instead of registering the route as fastify.get('/'), you could instead do fastify.get('') that would give you the expected result. Let me know if that helps resolve the issue.

nirendrashakya avatar Nov 01 '23 21:11 nirendrashakya