Skip to content

Commit d4c7cf3

Browse files
authored
docs: add POST modification example and test
1 parent e60bbc3 commit d4c7cf3

3 files changed

Lines changed: 105 additions & 77 deletions

File tree

README.md

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,25 +516,55 @@ server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
516516

517517
## Intercept and manipulate requests
518518

519-
Intercept requests from downstream by defining `onProxyReq` in `createProxyMiddleware`.
519+
Intercept requests from downstream by defining `on.proxyReq` in `createProxyMiddleware`.
520520

521-
Currently the only pre-provided request interceptor is `fixRequestBody`, which is used to fix proxied POST requests when `bodyParser` is applied before this middleware.
521+
Use the pre-provided request interceptor `fixRequestBody` to fix proxied POST requests when `bodyParser` is applied before this middleware.
522522

523-
Example:
523+
**Fix POST request:**
524524

525525
```javascript
526526
import { createProxyMiddleware, fixRequestBody } from 'http-proxy-middleware';
527527

528528
const proxy = createProxyMiddleware({
529529
/**
530-
* Fix bodyParser
530+
* Fix POST request when `bodyParser` is used
531531
**/
532532
on: {
533533
proxyReq: fixRequestBody,
534534
},
535535
});
536536
```
537537

538+
**Modify POST request:**
539+
540+
```javascript
541+
import bodyParser from 'body-parser';
542+
import { createProxyMiddleware, fixRequestBody } from 'http-proxy-middleware';
543+
544+
app.use(bodyParser.json());
545+
546+
app.use(
547+
'/api',
548+
createProxyMiddleware({
549+
target: 'http://www.example.org',
550+
changeOrigin: true,
551+
on: {
552+
proxyReq: (proxyReq, req) => {
553+
if (req.method !== 'POST' || !req.body) {
554+
return;
555+
}
556+
557+
// mutate parsed request body
558+
req.body.injected = 'server-only';
559+
560+
// write mutated body to the proxied request
561+
fixRequestBody(proxyReq, req);
562+
},
563+
},
564+
}),
565+
);
566+
```
567+
538568
## Intercept and manipulate responses
539569

540570
Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)

recipes/modify-post.md

Lines changed: 38 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,46 @@
1-
## Modify Post Parameters:
1+
## Modify POST body
22

3-
The code example below illustrates how to modify POST body data prior to forwarding to the proxy target.
4-
Key to this example is the _"OnProxyReq"_ event handler that creates a new POST body that can be manipulated to format the POST data as required. For example: inject new POST parameters that should only be visible server side.
3+
Use `body-parser` to populate `req.body`, mutate that object in `on.proxyReq`, then call `fixRequestBody` so the updated body is written to the outgoing proxy request.
54

6-
This example uses the _"body-parser"_ module in the main app to create a req.body object with the decoded POST parameters. Side note - the code below will allow _"http-proxy-middleware"_ to work with _"body-parser"_.
7-
8-
Since this only modifies the request body stream the original POST body parameters remain in tact, so any POST data changes will not be sent back in the response to the client.
9-
10-
## Example:
5+
### Minimal example
116

127
```js
13-
'use strict';
14-
158
import express from 'express';
16-
import { createProxyMiddleware } from 'http-proxy-middleware';
17-
18-
const router = express.Router();
19-
20-
const proxy_options = {
21-
target: 'http://localhost:8080',
22-
pathRewrite: {
23-
'^/docs': '/java/rep/server1', // Host path & target path conversion
24-
},
25-
pathFilter: function (path, req) {
26-
return path.match('^/docs') && (req.method === 'GET' || req.method === 'POST');
27-
},
28-
on: {
29-
error(err, req, res) {
30-
res.writeHead(500, {
31-
'Content-Type': 'text/plain',
32-
});
33-
res.end('Something went wrong. And we are reporting a custom error message.' + err);
9+
import bodyParser from 'body-parser';
10+
import { createProxyMiddleware, fixRequestBody } from 'http-proxy-middleware';
11+
12+
const app = express();
13+
14+
app.use(bodyParser.json());
15+
16+
app.use(
17+
'/search',
18+
createProxyMiddleware({
19+
target: 'http://localhost:4000',
20+
changeOrigin: true,
21+
on: {
22+
proxyReq(proxyReq, req) {
23+
if (req.method !== 'POST' || !req.body) {
24+
return;
25+
}
26+
27+
// 1) modify the parsed body
28+
req.body.limit = 25;
29+
req.body.filters = ['public'];
30+
31+
// 2) optional server-only header
32+
proxyReq.setHeader('x-api-key', process.env.SEARCH_API_KEY ?? '');
33+
34+
// 3) write modified body to the proxied request
35+
fixRequestBody(proxyReq, req);
36+
},
3437
},
35-
proxyReq(proxyReq, req, res) {
36-
if (req.method == 'POST' && req.body) {
37-
// Add req.body logic here if needed....
38-
39-
// ....
40-
41-
// Remove body-parser body object from the request
42-
if (req.body) delete req.body;
43-
44-
// Make any needed POST parameter changes
45-
let body = new Object();
46-
47-
body.filename = 'reports/statistics/summary_2016.pdf';
48-
body.routeId = 's003b012d002';
49-
body.authId = 'bac02c1d-258a-4177-9da6-862580154960';
50-
51-
// URI encode JSON object
52-
body = Object.keys(body)
53-
.map(function (key) {
54-
return encodeURIComponent(key) + '=' + encodeURIComponent(body[key]);
55-
})
56-
.join('&');
57-
58-
// Update header
59-
proxyReq.setHeader('content-type', 'application/x-www-form-urlencoded');
60-
proxyReq.setHeader('content-length', body.length);
61-
62-
// Write out body changes to the proxyReq stream
63-
proxyReq.write(body);
64-
proxyReq.end();
65-
}
66-
},
67-
},
68-
};
69-
70-
// Proxy configuration
71-
const proxy = createProxyMiddleware(proxy_options);
72-
73-
/* GET home page. */
74-
router.get('/', function (req, res, next) {
75-
res.render('index', { title: 'Node.js Express Proxy Test' });
76-
});
38+
}),
39+
);
40+
```
7741
78-
router.all('/docs', proxy);
42+
### Essential points
7943
80-
module.exports = router;
81-
```
44+
- If `body-parser` runs before the proxy, the original request stream has already been consumed.
45+
- Updating `req.body` alone is not enough; call `fixRequestBody(proxyReq, req)` to forward the modified payload.
46+
- Keep mutations in `proxyReq` focused on request shaping (fields/headers) and avoid unrelated app logic in this handler.

test/e2e/http-proxy-middleware.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,39 @@ describe('E2E http-proxy-middleware', () => {
131131
await agent.post('/api').send({ foo: 'bar', bar: 'baz', doubleByte: '文' }).expect(200);
132132
});
133133

134+
it('should proxy modified POST body from proxyReq when bodyParser is used (#942)', async () => {
135+
agent = request(
136+
createApp(
137+
bodyParser.json(),
138+
createProxyMiddleware({
139+
target: mockTargetServer.url,
140+
pathFilter: '/api',
141+
on: {
142+
proxyReq: (proxyReq, req) => {
143+
req.body.search = `${req.body.search} + context-filter`;
144+
req.body.top = 25;
145+
req.body.filters = ['public', 'active'];
146+
proxyReq.setHeader('x-api-key', 'server-secret');
147+
fixRequestBody(proxyReq, req);
148+
},
149+
},
150+
}),
151+
),
152+
);
153+
154+
await mockTargetServer.forPost('/api').thenCallback(async (req) => {
155+
expect(await req.body.getJson()).toEqual({
156+
search: 'hello + context-filter',
157+
top: 25,
158+
filters: ['public', 'active'],
159+
});
160+
expect(req.headers['x-api-key']).toBe('server-secret');
161+
return { statusCode: 200, body: 'OK' };
162+
});
163+
164+
await agent.post('/api').send({ search: 'hello', top: 5 }).expect(200, 'OK');
165+
});
166+
134167
it('should detect bodyParser usage leading to ECONNRESET error', async () => {
135168
const logSpy = vi.spyOn(console, 'error');
136169

0 commit comments

Comments
 (0)