NodeJS - Edit and proxy a multipart/form-data request
Asked Answered
S

1

5

I have a microservice that proxies every request adding one more field to it. With normal requests it's very easy, just add the field in the request.body and properly set the headers but for multipart/form-data requests I'm in trouble since days because if I add a field in the request.body, it will disappear.

const router = express()
const routes = require('~/routes')
const passport = require('passport')
const proxy = require('http-proxy-middleware')

router.use(passport.initialize())
require('./modules/passport-jwt')(passport)

router.use('/', routes)

router.use(
    '/account',
    passport.authenticate('jwt', { session: false }),
    proxy({
        target: process.env.ACCOUNT_SERVICE,
        pathRewrite: { '/account': '/' },
        onProxyReq: restream
    })
)

const restream = async function (proxyReq, req, res, options) {
    if (req.user) {
        if (
            req.headers['content-type'] &&
            req.headers['content-type'].match(/^multipart\/form-data/)
        ) {
            req.body.reqUser = req.user
        } else {
            const requestBody = JSON.stringify({ ...req.body, reqUser: req.user })
            proxyReq.setHeader('Content-Type', 'application/json')
            proxyReq.setHeader('Content-Length', Buffer.byteLength(requestBody))
            proxyReq.write(requestBody)
        }
    }
}

When the request arrives to the other microservice, the request.body is empty and after it will be written by multer that will put multipart/form-data params into the request.body.

I really need a solution that let me append a field into a multipart/form-data request in the proxy restream function.

I tried everything to succeed in this but I'm stuck. I hope everything is clear from my side. Don't be afraid to ask for more details if you need them. I'm begging for your help.

Snooze answered 18/10, 2019 at 12:56 Comment(0)
S
8

I finally figured out how to succeed. My code now looks like this:

const router = express()
const routes = require('~/routes')
const passport = require('passport')
const proxy = require('http-proxy-middleware')

router.use(passport.initialize())
require('./modules/passport-jwt')(passport)

router.use('/', routes)

router.use(
    '/account',
    passport.authenticate('jwt', { session: false }),
    proxy({
        target: process.env.ACCOUNT_SERVICE,
        pathRewrite: { '/account': '/' },
        onProxyReq: restream
    })
)

const restream = async function (proxyReq, req, res, options) {
    if (req.user) {
        if (
            req.headers['content-type'] &&
            req.headers['content-type'].match(/^multipart\/form-data/)
        ) {
            // build a string in multipart/form-data format with the data you need
            const formdataUser =
                `--${request.headers['content-type'].replace(/^.*boundary=(.*)$/, '$1')}\r\n` +
                `Content-Disposition: form-data; name="reqUser"\r\n` +
                `\r\n` +
                `${JSON.stringify(request.user)}\r\n`

            // set the new content length
            proxyReq.setHeader(
                'Content-Length',
                parseInt(request.headers['content-length']) + Buffer.byteLength(formdataUser)
            )

            proxyReq.write(formdataUser)
        } else {
            const body = JSON.stringify({ ...req.body, reqUser: req.user })
            proxyReq.setHeader('Content-Type', 'application/json')
            proxyReq.setHeader('Content-Length', Buffer.byteLength(body))
            proxyReq.write(body)
        }
    }
}

As I wrote in the code comments:

  1. Build a string in multipart/form-data format that must look like this:

    ------WebKitFormBoundaryiBtoTWFkpAG6CgXO\r\n
    Content-Disposition: form-data; name="firstname"\r\n
    \r\n\
    Andrea\r\n
    

    (In my code I stringified the data because was an object);

  2. Set the header 'Content-Length' by adding the above string's byte length to the original request length;

  3. Use proxyReq.write function to send the new data.

Snooze answered 24/10, 2019 at 7:44 Comment(1)
Thanks for this. You made my day.Heedful

© 2022 - 2024 — McMap. All rights reserved.