Skip to content

Commit

Permalink
feat: rewrite URLs on the request object
Browse files Browse the repository at this point in the history
On the Express.js request object, this rewrites req.url in order to make req.path reflect the
modified req.params

BREAKING CHANGE: This modifies the request object passed on to other middlewares
  • Loading branch information
Aposhian committed Sep 9, 2020
1 parent 9ee7d54 commit cc179a5
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
coverage/
node_modules/
node_modules/
.vscode/
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ app.get('/:id', (req, res) => {
...
})
})
```
```

### URL rewriting
In order to match up with downstream middleware or handlers, this middleware also rewrites the url variables on the Express.js request object.

This is done by rewriting `req.url`, which is parsed to produce `req.path`. This does not modify `req.baseUrl` or `req.originalUrl`.
11 changes: 10 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const required = (name) => {
throw new Error(`Required parameter: ${name}`)
}

const rewriteUrl = ({ req, alias, value }) => {
const re = new RegExp(`(?<=/)${alias}(?=/)?`)
return req.url.replace(re, value)
}

/**
* Allows using route parameter aliases in a request. Aliases are mapped to values
* in the payload of a JWT provided elsewhere in the request
Expand Down Expand Up @@ -93,7 +98,11 @@ module.exports = ({

const middleware = (req, _, next) => {
if (req.params[paramName] === alias) {
req.params[paramName] = getParamValue({ req, tokenLocation, tokenName, payloadKey })
const value = getParamValue({ req, tokenLocation, tokenName, payloadKey })

req.params[paramName] = value

req.url = rewriteUrl({ req, alias, value })
}

next()
Expand Down
80 changes: 56 additions & 24 deletions test/test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ const jwt = require('jsonwebtoken')

const any = () => Math.random().toString(36).substring(2, 15)

const echoParams = (req, res) => res.json(req.params)
const echoRequestObject = (req, res) => res.json({
params: req.params,
baseUrl: req.baseUrl,
path: req.path,
originalUrl: req.originalUrl,
url: req.url
})

const createApp = ({ middlewares = [], paramNames = [] }) => {
const app = express()

// ex: '/:param1/:param2'
const route = paramNames.map(paramName => `/:${paramName}`).join()

app.post(route, ...middlewares, echoParams)
app.post(route, ...middlewares, echoRequestObject)

return app
}
Expand Down Expand Up @@ -92,15 +98,35 @@ describe('routeParamAlias middleware', () => {

const token = jwt.sign({ [paramName]: headerParam }, 'super_secret')

const route = `/${routeParam}`

const res = await request(app)
.post(`/${routeParam}`)
.post(route)
.set('x-param', token)
.expect(200)
.expect('Content-Type', /json/)

expect(res.body).toMatchObject({
const {
params,
originalUrl,
baseUrl,
url,
path
} = res.body

expect(params).toMatchObject({
[paramName]: isAlias ? headerParam : routeParam
})

expect(originalUrl).toEqual(route)

expect(originalUrl).toEqual(route)
expect(baseUrl).toEqual('')

const expectedRoute = isAlias ? route.replace(alias, headerParam) : route

expect(url).toEqual(expectedRoute)
expect(path).toEqual(expectedRoute)
})

it('should return a 4xx error if the token does not contain the parameter', async () => {
Expand All @@ -111,19 +137,15 @@ describe('routeParamAlias middleware', () => {
.set('x-param', token)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})

it('should return a 4xx error if the token does not exist', async () => {
const res = await request(app)
.post(`/${alias}`)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})

it('should return a 4xx error if the query parameter is not a JWT', async () => {
Expand All @@ -134,9 +156,7 @@ describe('routeParamAlias middleware', () => {
.set('x-param', token)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})
})

Expand Down Expand Up @@ -166,14 +186,32 @@ describe('routeParamAlias middleware', () => {

const token = jwt.sign({ [paramName]: queryParam }, 'super_secret')

const route = `/${routeParam}`

const routeWithQuery = `${route}?${tokenName}=${token}`

const res = await request(app)
.post(`/${routeParam}?${tokenName}=${token}`)
.post(routeWithQuery)
.expect(200)
.expect('Content-Type', /json/)

expect(res.body).toMatchObject({
const {
params,
originalUrl,
baseUrl,
url,
path
} = res.body

expect(params).toMatchObject({
[paramName]: isAlias ? queryParam : routeParam
})

expect(originalUrl).toEqual(routeWithQuery)
expect(baseUrl).toEqual('')

expect(url).toEqual(isAlias ? routeWithQuery.replace(alias, queryParam) : routeWithQuery)
expect(path).toEqual(isAlias ? route.replace(alias, queryParam) : route)
})

it('should return a 4xx error if the token does not contain the parameter', async () => {
Expand All @@ -183,19 +221,15 @@ describe('routeParamAlias middleware', () => {
.post(`/${alias}?${tokenName}=${token}`)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})

it('should return a 4xx error if the token does not exist', async () => {
const res = await request(app)
.post(`/${alias}`)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})

it('should return a 4xx error if the query parameter is not a JWT', async () => {
Expand All @@ -205,9 +239,7 @@ describe('routeParamAlias middleware', () => {
.post(`/${alias}?${tokenName}=${token}`)
.expect(400)

expect(res.body).not.toMatchObject({
[paramName]: expect.any(String)
})
expect(res.body.params).toBeFalsy()
})
})
})

0 comments on commit cc179a5

Please sign in to comment.