diff --git a/__tests__/integration/one-app.spec.js b/__tests__/integration/one-app.spec.js index 16435632..a594725c 100644 --- a/__tests__/integration/one-app.spec.js +++ b/__tests__/integration/one-app.spec.js @@ -1300,6 +1300,27 @@ describe('Tests that can run against either local Docker setup or remote One App sendingData: 'in POSTs', }); }); + test('app passes urlencoded POST data to modules via vitruvius', async () => { + const response = await fetch( + `${appInstanceUrls.fetchUrl}/vitruvius`, + { + ...defaultFetchOpts, + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + body: 'legacy=application&sendingData=in POSTs', + } + ); + + const pageHtml = await response.text(); + const data = JSON.parse(pageHtml.match(/
([^<]+)<\/pre>/)[1].replace(/"/g, '"')); + expect(data).toHaveProperty('req.body'); + expect(data.req.body).toEqual({ + legacy: 'application', + sendingData: 'in POSTs', + }); + }); describe('routing', () => { test('IndexRedirect redirects', async () => { diff --git a/__tests__/server/ssrServer.spec.js b/__tests__/server/ssrServer.spec.js index b778f04b..7afba586 100644 --- a/__tests__/server/ssrServer.spec.js +++ b/__tests__/server/ssrServer.spec.js @@ -17,7 +17,10 @@ import request from 'supertest'; jest.mock('express'); -jest.mock('body-parser', () => ({ json: jest.fn(() => (req, res, next) => next()) })); +jest.mock('body-parser', () => ({ + json: jest.fn(() => (req, res, next) => next()), + urlencoded: jest.fn(() => (req, res, next) => next()), +})); jest.mock('../../src/server/middleware/clientErrorLogger'); jest.mock('../../src/server/middleware/setAppVersionHeader'); jest.mock('../../src/server/middleware/addSecurityHeaders'); @@ -109,13 +112,14 @@ describe('ssrServer', () => { let addFrameOptionsHeader; let addCacheHeaders; let json; + let urlencoded; let forwardedHeaderParser; let serviceWorker; let webManifest; let offline; function loadServer() { - ({ json } = require('body-parser')); + ({ json, urlencoded } = require('body-parser')); clientErrorLogger = require('../../src/server/middleware/clientErrorLogger').default; setAppVersionHeader = require('../../src/server/middleware/setAppVersionHeader').default; addSecurityHeaders = require('../../src/server/middleware/addSecurityHeaders').default; @@ -424,6 +428,25 @@ describe('ssrServer', () => { done(); }); }); + it('should configure urlencoded parsing with a maximum limit for render post pre-flight options calls', (done) => { + request(loadServer()) + .options('/route') + .end(() => { + expect(urlencoded).toBeCalled(); + expect(json.mock.calls[1][0]).toHaveProperty('limit', '0kb'); + done(); + }); + }); + + it('should configure json urlencoded with a maximum limit for render post calls', (done) => { + request(loadServer()) + .post('/route') + .end(() => { + expect(urlencoded).toBeCalled(); + expect(json.mock.calls[2][0]).toHaveProperty('limit', '15kb'); + done(); + }); + }); describe('cors for render post calls', () => { it('pre-flight OPTIONS should not respond with CORS headers', (done) => { diff --git a/docs/recipes/Making-An-Api-Call.md b/docs/recipes/Making-An-Api-Call.md index 9c4a0a17..a03cf117 100644 --- a/docs/recipes/Making-An-Api-Call.md +++ b/docs/recipes/Making-An-Api-Call.md @@ -6,4 +6,13 @@ Recipe is forthcoming. +## POST +To enable post set [`ONE_ENABLE_POST_TO_MODULE_ROUTES`](../api/server/Environment-Variables.md#ONE_ENABLE_POST_TO_MODULE_ROUTES) environment variable. +Request body must be either a JSON object or FormData of less than 15KB in size and is passed as props to your module. + +Supported media types: +- `application/json` +- `application/x-www-form-urlencoded` + + [☝️ Return To Top](#Making-An-Api-Call) diff --git a/src/server/ssrServer.js b/src/server/ssrServer.js index 2f6b42e1..f9df9eb3 100644 --- a/src/server/ssrServer.js +++ b/src/server/ssrServer.js @@ -23,7 +23,7 @@ import path from 'path'; import express from 'express'; import compression from 'compression'; import cookieParser from 'cookie-parser'; -import { json } from 'body-parser'; +import { json, urlencoded } from 'body-parser'; import helmet from 'helmet'; import cors from 'cors'; @@ -96,6 +96,7 @@ export function createApp({ enablePostToModuleRoutes = false } = {}) { '*', addSecurityHeaders, json({ limit: '0kb' }), // there should be no body + urlencoded({ limit: '0kb' }), // there should be no body cors({ origin: false }) // disable CORS ); @@ -103,6 +104,7 @@ export function createApp({ enablePostToModuleRoutes = false } = {}) { '*', addSecurityHeaders, json({ limit: '15kb' }), + urlencoded({ limit: '15kb' }), addFrameOptionsHeader, createRequestStore(oneApp, { useBodyForBuildingTheInitialState: true }), createRequestHtmlFragment(oneApp),