Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS - Edit and proxy a multipart/form-data request

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.

like image 748
Andrea d'Argenio Avatar asked Jan 26 '23 17:01

Andrea d'Argenio


1 Answers

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.

like image 182
Andrea d'Argenio Avatar answered Jan 28 '23 23:01

Andrea d'Argenio