Http-proxy-middleware: app.use(bodyParser.json()) breaks http-proxy-middleware for HTTP POST JSON with express

Created on 21 Jan 2019  路  9Comments  路  Source: chimurai/http-proxy-middleware

For a test case of the following

curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/html' -d '{ "$class": "org.accordproject.cicero.contract.AccordContractState", "stateId": "test2" }' 'http://domain/path'

Where http://domain/path uses http-proxy-middleware with express then the introduction of app.use(bodyParser.json()) for express with this http-proxy-middleware, all together the app.use(bodyParser.json()) breaks this.

This is an unexpected surprise and finding this cause of this error was difficult.

Most helpful comment

FYI I worked around this issue like so:

options.onProxyReq = (proxyReq, req: Request, res) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  const writeBody = (bodyData: string) => {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  };

  if (contentType === 'application/json') {
    writeBody(JSON.stringify(req.body));
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    writeBody(querystring.stringify(req.body));
  }
}

All 9 comments

FYI I worked around this issue like so:

options.onProxyReq = (proxyReq, req: Request, res) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  const writeBody = (bodyData: string) => {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  };

  if (contentType === 'application/json') {
    writeBody(JSON.stringify(req.body));
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    writeBody(querystring.stringify(req.body));
  }
}

contentType maybe contain chareset (contentType: 'application/json; charset:utf-8').
So, should be change contentType === 'application/json' to contentType.includes('application/json')

FYI I worked around this issue like so:

options.onProxyReq = (proxyReq, req: Request, res) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  const writeBody = (bodyData: string) => {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  };

  if (contentType === 'application/json') {  // contentType.includes('application/json')
    writeBody(JSON.stringify(req.body));
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    writeBody(querystring.stringify(req.body));
  }
}

I instead excluded the proxied requests from the body parser middleware: https://stackoverflow.com/questions/27117337/exclude-route-from-express-middleware

You just need to reorder the middlewares;
put bodyParser after all proxy middlewares and routes without proxy after bodyParser
ex:

app.use('/proxy/routex', myProxy);
app.use('/proxy/routey', myProxy);

... //then, after all proxys
app.use(bodyParser.json());

//and now, all no proxy routes
app.use('/api', (req,res)=>{
//TODO
});

app.get('/api/test', (req,res)=>{
//TODO
});

Same with polka.
@sostenesg7's solution worked.

FYI I worked around this issue like so:

options.onProxyReq = (proxyReq, req: Request, res) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  const writeBody = (bodyData: string) => {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  };

  if (contentType === 'application/json') {
    writeBody(JSON.stringify(req.body));
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    writeBody(querystring.stringify(req.body));
  }
}

Saved my soul

Just a use case and slight variations on the work around.

I'm using the Okta Middlware for Node library, which requires body parser, but then I want to include the user's access token in proxied api calls, according to the token relay pattern:
https://developer.okta.com/blog/2020/08/14/spring-gateway-patterns

The requires me to implement the aforementioned fix, but I don't see a requirement to define writeBody as a capture so mine looks like:

// From poxy options
onProxyReq: (targetRequest: ClientRequest, clientRequest: Request) => {
    // Headers must be set before doing anything to the body.
    if (clientRequest?.userContext?.tokens?.access_token) {
      targetRequest.setHeader('Authorization', 'Bearer ' + clientRequest.userContext.tokens.access_token);
    }
    fixBody(targetRequest, clientRequest);
}


/**
 * http-proxy-middleware is not compatible with bodyparser and must appear before it without this fix.
 * @see see https://github.com/chimurai/http-proxy-middleware/issues/320
 * @param proxyReq
 * @param req
 */
function fixBody(proxyReq: ClientRequest, req: Request): void {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }
  const contentType = proxyReq.getHeader('Content-Type') as string;
  if (contentType.includes('application/json')) {
    writeBody(proxyReq, JSON.stringify(req.body));
  }
  if (contentType === 'application/x-www-form-urlencoded') {
    writeBody(proxyReq, querystring.stringify(req.body));
  }
}

function writeBody(proxyReq: ClientRequest, bodyData: string) {
  proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
  proxyReq.write(bodyData);
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dzz9143 picture dzz9143  路  3Comments

noahgrant picture noahgrant  路  4Comments

gajus picture gajus  路  8Comments

kimmobrunfeldt picture kimmobrunfeldt  路  5Comments

maks-rafalko picture maks-rafalko  路  8Comments