first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
## Unreleased
## 4.2.0
- support all text content types (#179)
- adds TypeScript integration testing (#177)
- removes testing against EOL Node.js verions
## 4.1.3
Reverted changes introduced in 4.1.2. Now 4.1.3 is effectively the same as 4.1.1.
## 4.1.2
Bad TypeScript definition file change (#173). Do not use this version.
## 4.1.1
- adds support for JSON Patch, JSON API and CSP report out of the box:
- application/json-patch+json (https://tools.ietf.org/html/rfc6902)
- application/vnd.api+json (https://jsonapi.org/)
- application/csp-report (https://www.w3.org/TR/CSP2/#violation-reports)
## 4.1.0
- adds `parsedMethods` option to specify which request methods will be parsed
- deprecates `strict` option, which will be removed in koa-body 5.0.0
### Migrating from 4.x.x to 4.1.0
Migration from prior 4.x.x versions is strightforward.
- If you used `strict: true`, simply remove this option. The new defaults will behave the same way.
- If you used `strict: false`, set `parsedMethods` to the set of methods you would like to parse. For example, `parsedMethods: ['GET', 'POST', 'PUT', 'PATCH']`
## 4.0.0 - 4.0.8 - Summary of Changes
- mutliple type definition updates
- adds `includeUnparsed` option to get raw body
## Breaking Changes in v3/4
To address a potential [security vulnerability](https://snyk.io/vuln/npm:koa-body:20180127):
- The `files` property has been moved to `ctx.request.files`. In prior versions, `files` was a property of `ctx.request.body`.
- The `fields` property is flatten (merged) into `ctx.request.body`. In prior versions, `fields` was a property of `ctx.request.body`.
If you do not use multipart uploads, no changes to your code need to be made.
Versions 1 and 2 of `koa-body` are deprecated and replaced with versions 3 and 4, respectively.
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Charlike Mike Reagent <mameto_100@mail.bg> and Daryl Lau <daryl@weak.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+174
View File
@@ -0,0 +1,174 @@
koa-body [![Build Status](https://travis-ci.org/dlau/koa-body.svg?branch=koa2)](https://travis-ci.org/dlau/koa-body) [![Dependencies Status](https://david-dm.org/dlau/koa-body/status.svg)](https://david-dm.org/dlau/koa-body) [![KoaJs Slack](https://img.shields.io/badge/Koa.Js-Slack%20Channel-Slack.svg?longCache=true)](https://communityinviter.com/apps/koa-js/koajs)
================
> A full-featured [`koa`](https://github.com/koajs/koa) body parser middleware. Supports `multipart`, `urlencoded`, and `json` request bodies. Provides the same functionality as Express's bodyParser - [`multer`](https://github.com/expressjs/multer).
## Install
>Install with [npm](https://github.com/npm/npm)
```
npm install koa-body
```
## Features
- can handle requests such as:
* **multipart/form-data**
* **application/x-www-urlencoded**
* **application/json**
* **application/json-patch+json**
* **application/vnd.api+json**
* **application/csp-report**
* **text/xml**
- option for patch to Koa or Node, or either
- file uploads
- body, fields and files size limiting
## Hello World - Quickstart
```sh
npm install koa koa-body # Note that Koa requires Node.js 7.6.0+ for async/await support
```
index.js:
```js
const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();
app.use(koaBody());
app.use(ctx => {
ctx.body = `Request Body: ${JSON.stringify(ctx.request.body)}`;
});
app.listen(3000);
```
```sh
node index.js
curl -i http://localhost:3000/users -d "name=test"
```
Output:
```text
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 29
Date: Wed, 03 May 2017 02:09:44 GMT
Connection: keep-alive
Request Body: {"name":"test"}%
```
**For a more comprehensive example, see** `examples/multipart.js`
## Usage with [koa-router](https://github.com/alexmingoia/koa-router)
It's generally better to only parse the body as needed, if using a router that supports middleware composition, we can inject it only for certain routes.
```js
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();
const koaBody = require('koa-body');
router.post('/users', koaBody(),
(ctx) => {
console.log(ctx.request.body);
// => POST body
ctx.body = JSON.stringify(ctx.request.body);
}
);
app.use(router.routes());
app.listen(3000);
console.log('curl -i http://localhost:3000/users -d "name=test"');
```
## Usage with unsupported text body type
For unsupported text body type, for example, `text/xml`, you can use the unparsed request body at `ctx.request.body`. For the text content type, the `includeUnparsed` setting is not required.
```js
// xml-parse.js:
const Koa = require('koa');
const koaBody = require('koa-body');
const convert = require('xml-js');
const app = new Koa();
app.use(koaBody());
app.use(ctx => {
const obj = convert.xml2js(ctx.request.body)
ctx.body = `Request Body: ${JSON.stringify(obj)}`;
});
app.listen(3000);
```
```sh
node xml-parse.js
curl -i http://localhost:3000/users -H "Content-Type: text/xml" -d '<?xml version="1.0"?><catalog id="1"></catalog>'
```
Output:
```text
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 135
Date: Tue, 09 Jun 2020 11:17:38 GMT
Connection: keep-alive
Request Body: {"declaration":{"attributes":{"version":"1.0"}},"elements":[{"type":"element","name":"catalog","attributes":{"id":"1"}}]}%
```
## Options
> Options available for `koa-body`. Four custom options, and others are from `raw-body` and `formidable`.
- `patchNode` **{Boolean}** Patch request body to Node's `ctx.req`, default `false`
- `patchKoa` **{Boolean}** Patch request body to Koa's `ctx.request`, default `true`
- `jsonLimit` **{String|Integer}** The byte (if integer) limit of the JSON body, default `1mb`
- `formLimit` **{String|Integer}** The byte (if integer) limit of the form body, default `56kb`
- `textLimit` **{String|Integer}** The byte (if integer) limit of the text body, default `56kb`
- `encoding` **{String}** Sets encoding for incoming form fields, default `utf-8`
- `multipart` **{Boolean}** Parse multipart bodies, default `false`
- `urlencoded` **{Boolean}** Parse urlencoded bodies, default `true`
- `text` **{Boolean}** Parse text bodies, such as XML, default `true`
- `json` **{Boolean}** Parse JSON bodies, default `true`
- `jsonStrict` **{Boolean}** Toggles co-body strict mode; if set to true - only parses arrays or objects, default `true`
- `includeUnparsed` **{Boolean}** Toggles co-body returnRawBody option; if set to true, for form encodedand and JSON requests the raw, unparsed requesty body will be attached to `ctx.request.body` using a `Symbol`, default `false`
- `formidable` **{Object}** Options to pass to the formidable multipart parser
- `onError` **{Function}** Custom error handle, if throw an error, you can customize the response - onError(error, context), default will throw
- `strict` **{Boolean}** ***DEPRECATED*** If enabled, don't parse GET, HEAD, DELETE requests, default `true`
- `parsedMethods` **{String[]}** Declares the HTTP methods where bodies will be parsed, default `['POST', 'PUT', 'PATCH']`. Replaces `strict` option.
## A note about `parsedMethods`
> see [http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-19#section-6.3](http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-19#section-6.3)
- `GET`, `HEAD`, and `DELETE` requests have no defined semantics for the request body, but this doesn't mean they may not be valid in certain use cases.
- koa-body is strict by default, parsing only `POST`, `PUT`, and `PATCH` requests
## File Support
Uploaded files are accessible via `ctx.request.files`.
## A note about unparsed request bodies
Some applications require crytopgraphic verification of request bodies, for example webhooks from slack or stripe. The unparsed body can be accessed if `includeUnparsed` is `true` in koa-body's options. When enabled, import the symbol for accessing the request body from `unparsed = require('koa-body/unparsed.js')`, or define your own accessor using `unparsed = Symbol.for('unparsedBody')`. Then the unparsed body is available using `ctx.request.body[unparsed]`.
## Some options for formidable
> See [node-formidable](https://github.com/felixge/node-formidable) for a full list of options
- `maxFields` **{Integer}** Limits the number of fields that the querystring parser will decode, default `1000`
- `maxFieldsSize` **{Integer}** Limits the amount of memory all fields together (except files) can allocate in bytes. If this value is exceeded, an 'error' event is emitted, default `2mb (2 * 1024 * 1024)`
- `uploadDir` **{String}** Sets the directory for placing file uploads in, default `os.tmpDir()`
- `keepExtensions` **{Boolean}** Files written to `uploadDir` will include the extensions of the original files, default `false`
- `hash` **{String}** If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`, default `false`
- `multiples` **{Boolean}** Multiple file uploads or no, default `true`
- `onFileBegin` **{Function}** Special callback on file begin. The function is executed directly by formidable. It can be used to rename files before saving them to disk. [See the docs](https://github.com/felixge/node-formidable#filebegin)
## Changelog
Please see the [Changelog](./CHANGELOG.md) for a summary of changes.
## Tests
```
$ npm test
```
## License
The MIT License, 2014 [Charlike Mike Reagent](https://github.com/tunnckoCore) ([@tunnckoCore](https://twitter.com/tunnckoCore)) and [Daryl Lau](https://github.com/dlau) ([@daryllau](https://twitter.com/daryllau))
+168
View File
@@ -0,0 +1,168 @@
import * as Koa from "koa";
import { Files } from 'formidable';
declare module "koa" {
interface Request extends Koa.BaseRequest {
body?: any;
files?: Files;
}
}
declare namespace koaBody {
interface IKoaBodyFormidableOptions {
/**
* {Integer} Limits the amount of memory all fields together (except files) can allocate in bytes. If this value is exceeded, an 'error' event is emitted. The default size is 20MB.
*/
maxFileSize?: number;
/**
* {Integer} Limits the number of fields that the querystring parser will decode, default 1000
*/
maxFields?: number;
/**
* {Integer} Limits the amount of memory all fields together (except files) can allocate in bytes.
* If this value is exceeded, an 'error' event is emitted, default 2mb (2 * 1024 * 1024)
*/
maxFieldsSize?: number;
/**
* {String} Sets the directory for placing file uploads in, default os.tmpDir()
*/
uploadDir?: string;
/**
* {Boolean} Files written to uploadDir will include the extensions of the original files, default false
*/
keepExtensions?: boolean;
/**
* {String} If you want checksums calculated for incoming files, set this to either 'sha1' or 'md5', default false
*/
hash?: string;
/**
* {Boolean} Multiple file uploads or no, default true
*/
multiples?: boolean;
/**
* {Function} Special callback on file begin. The function is executed directly by formidable.
* It can be used to rename files before saving them to disk. See https://github.com/felixge/node-formidable#filebegin
*/
onFileBegin?: (name: string, file: any) => void;
}
interface IKoaBodyOptions {
/**
* {Boolean} Patch request body to Node's ctx.req, default false
*
* Note: You can patch request body to Node or Koa in same time if you want.
*/
patchNode?: boolean;
/**
* {Boolean} Patch request body to Koa's ctx.request, default true
*
* Note: You can patch request body to Node or Koa in same time if you want.
*/
patchKoa?: boolean;
/**
* {String|Integer} The byte (if integer) limit of the JSON body, default 1mb
*/
jsonLimit?: string|number;
/**
* {String|Integer} The byte (if integer) limit of the form body, default 56kb
*/
formLimit?: string|number;
/**
* {String|Integer} The byte (if integer) limit of the text body, default 56kb
*/
textLimit?: string|number;
/**
* {String} Sets encoding for incoming form fields, default utf-8
*/
encoding?: string;
/**
* {Boolean} Parse multipart bodies, default false
*/
multipart?: boolean;
/**
* {Boolean} Parse urlencoded bodies, default true
*/
urlencoded?: boolean;
/**
* {Boolean} Parse text bodies, default true
*/
text?: boolean;
/**
* {Boolean} Parse json bodies, default true
*/
json?: boolean;
/**
* Toggles co-body strict mode; if true, only parses arrays or objects, default true
*/
jsonStrict?: boolean;
/**
* Toggles co-body returnRawBody mode; if true,
* the raw body will be available using a Symbol for 'unparsedBody'.
*
* ```
// Either:
const unparsed = require('koa-body/unparsed.js');
const unparsed = Symbol.for('unparsedBody');
// Then later, to access:
ctx.request.body[unparsed]
```
* default false
*/
includeUnparsed?: boolean;
/**
* {Object} Options to pass to the formidable multipart parser
*/
formidable?: IKoaBodyFormidableOptions;
/**
* {Function} Custom error handle, if throw an error, you can customize the response - onError(error, context), default will throw
*/
onError?: (err: Error, ctx: Koa.Context) => void;
/**
* {Boolean} If enabled, don't parse GET, HEAD, DELETE requests; deprecated.
*
* GET, HEAD, and DELETE requests have no defined semantics for the request body,
* but this doesn't mean they may not be valid in certain use cases.
* koa-body is strict by default
*
* see http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-19#section-6.3
*/
strict?: boolean;
/**
* {String[]} What HTTP methods to enable body parsing for; should be used in preference to strict mode.
*
* GET, HEAD, and DELETE requests have no defined semantics for the request body,
* but this doesn't mean they may not be valid in certain use cases.
* koa-body will only parse HTTP request bodies for POST, PUT, and PATCH by default
*
* see http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-19#section-6.3
*/
parsedMethods?: string[];
}
}
declare function koaBody (options?: koaBody.IKoaBodyOptions): Koa.Middleware<{}, {}>;
export = koaBody;
+211
View File
@@ -0,0 +1,211 @@
/**
* koa-body - index.js
* Copyright(c) 2014
* MIT Licensed
*
* @author Daryl Lau (@dlau)
* @author Charlike Mike Reagent (@tunnckoCore)
* @api private
*/
'use strict';
/**
* Module dependencies.
*/
const buddy = require('co-body');
const forms = require('formidable');
const symbolUnparsed = require('./unparsed.js');
/**
* Expose `requestbody()`.
*/
module.exports = requestbody;
const jsonTypes = [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report'
];
/**
*
* @param {Object} options
* @see https://github.com/dlau/koa-body
* @api public
*/
function requestbody(opts) {
opts = opts || {};
opts.onError = 'onError' in opts ? opts.onError : false;
opts.patchNode = 'patchNode' in opts ? opts.patchNode : false;
opts.patchKoa = 'patchKoa' in opts ? opts.patchKoa : true;
opts.multipart = 'multipart' in opts ? opts.multipart : false;
opts.urlencoded = 'urlencoded' in opts ? opts.urlencoded : true;
opts.json = 'json' in opts ? opts.json : true;
opts.text = 'text' in opts ? opts.text : true;
opts.encoding = 'encoding' in opts ? opts.encoding : 'utf-8';
opts.jsonLimit = 'jsonLimit' in opts ? opts.jsonLimit : '1mb';
opts.jsonStrict = 'jsonStrict' in opts ? opts.jsonStrict : true;
opts.formLimit = 'formLimit' in opts ? opts.formLimit : '56kb';
opts.queryString = 'queryString' in opts ? opts.queryString : null;
opts.formidable = 'formidable' in opts ? opts.formidable : {};
opts.includeUnparsed = 'includeUnparsed' in opts ? opts.includeUnparsed : false
opts.textLimit = 'textLimit' in opts ? opts.textLimit : '56kb';
// @todo: next major version, opts.strict support should be removed
if (opts.strict && opts.parsedMethods) {
throw new Error('Cannot use strict and parsedMethods options at the same time.')
}
if ('strict' in opts) {
console.warn('DEPRECATED: opts.strict has been deprecated in favor of opts.parsedMethods.')
if (opts.strict) {
opts.parsedMethods = ['POST', 'PUT', 'PATCH']
} else {
opts.parsedMethods = ['POST', 'PUT', 'PATCH', 'GET', 'HEAD', 'DELETE']
}
}
opts.parsedMethods = 'parsedMethods' in opts ? opts.parsedMethods : ['POST', 'PUT', 'PATCH']
opts.parsedMethods = opts.parsedMethods.map(function (method) { return method.toUpperCase() })
return function (ctx, next) {
var bodyPromise;
// only parse the body on specifically chosen methods
if (opts.parsedMethods.includes(ctx.method.toUpperCase())) {
try {
if (opts.json && ctx.is(jsonTypes)) {
bodyPromise = buddy.json(ctx, {
encoding: opts.encoding,
limit: opts.jsonLimit,
strict: opts.jsonStrict,
returnRawBody: opts.includeUnparsed
});
} else if (opts.urlencoded && ctx.is('urlencoded')) {
bodyPromise = buddy.form(ctx, {
encoding: opts.encoding,
limit: opts.formLimit,
queryString: opts.queryString,
returnRawBody: opts.includeUnparsed
});
} else if (opts.text && ctx.is('text/*')) {
bodyPromise = buddy.text(ctx, {
encoding: opts.encoding,
limit: opts.textLimit,
returnRawBody: opts.includeUnparsed
});
} else if (opts.multipart && ctx.is('multipart')) {
bodyPromise = formy(ctx, opts.formidable);
}
} catch (parsingError) {
if (typeof opts.onError === 'function') {
opts.onError(parsingError, ctx);
} else {
throw parsingError;
}
}
}
bodyPromise = bodyPromise || Promise.resolve({});
return bodyPromise.catch(function(parsingError) {
if (typeof opts.onError === 'function') {
opts.onError(parsingError, ctx);
} else {
throw parsingError;
}
return next();
})
.then(function(body) {
if (opts.patchNode) {
if (isMultiPart(ctx, opts)) {
ctx.req.body = body.fields;
ctx.req.files = body.files;
} else if (opts.includeUnparsed) {
ctx.req.body = body.parsed || {};
if (! ctx.is('text/*')) {
ctx.req.body[symbolUnparsed] = body.raw;
}
} else {
ctx.req.body = body;
}
}
if (opts.patchKoa) {
if (isMultiPart(ctx, opts)) {
ctx.request.body = body.fields;
ctx.request.files = body.files;
} else if (opts.includeUnparsed) {
ctx.request.body = body.parsed || {};
if (! ctx.is('text/*')) {
ctx.request.body[symbolUnparsed] = body.raw;
}
} else {
ctx.request.body = body;
}
}
return next();
})
};
}
/**
* Check if multipart handling is enabled and that this is a multipart request
*
* @param {Object} ctx
* @param {Object} opts
* @return {Boolean} true if request is multipart and being treated as so
* @api private
*/
function isMultiPart(ctx, opts) {
return opts.multipart && ctx.is('multipart');
}
/**
* Donable formidable
*
* @param {Stream} ctx
* @param {Object} opts
* @return {Promise}
* @api private
*/
function formy(ctx, opts) {
return new Promise(function (resolve, reject) {
var fields = {};
var files = {};
var form = new forms.IncomingForm(opts);
form.on('end', function () {
return resolve({
fields: fields,
files: files
});
}).on('error', function (err) {
return reject(err);
}).on('field', function (field, value) {
if (fields[field]) {
if (Array.isArray(fields[field])) {
fields[field].push(value);
} else {
fields[field] = [fields[field], value];
}
} else {
fields[field] = value;
}
}).on('file', function (field, file) {
if (files[field]) {
if (Array.isArray(files[field])) {
files[field].push(file);
} else {
files[field] = [files[field], file];
}
} else {
files[field] = file;
}
});
if (opts.onFileBegin) {
form.on('fileBegin', opts.onFileBegin);
}
form.parse(ctx.req);
});
}
+65
View File
@@ -0,0 +1,65 @@
{
"name": "koa-body",
"version": "4.2.0",
"description": "A Koa body parser middleware. Supports multipart, urlencoded and JSON request bodies.",
"main": "index.js",
"types": "./index.d.ts",
"scripts": {
"test": "mocha test/unit/",
"examples-multer": "node examples/multer.js",
"examples-koa-router": "node examples/koa-router.js"
},
"author": {
"name": "Daryl Lau",
"email": "dlau00@gmail.com",
"url": "https://github.com/dlau"
},
"repository": {
"type": "git",
"url": "git://github.com/dlau/koa-body.git"
},
"keywords": [
"koa",
"urlencoded",
"multipart",
"json",
"body",
"parser",
"form"
],
"files": [
"LICENSE",
"README.md",
"index.js",
"index.d.ts",
"package.json",
"unparsed.js"
],
"dependencies": {
"@types/formidable": "^1.0.31",
"co-body": "^5.1.1",
"formidable": "^1.1.1"
},
"devDependencies": {
"@types/koa": "^2.0.39",
"koa": "^2.0.0",
"koa-router": "^7.0.1",
"mocha": "5.2.0",
"should": "13.2.1",
"sinon": "^7.2.2",
"supertest": "3.1.0"
},
"contributors": [
{
"name": "Daryl Lau",
"email": "dlau00@gmail.com",
"url": "https://github.com/dlau"
},
{
"name": "Charlike Mike Reagent",
"email": "mameto_100@mail.bg",
"url": "https://github.com/tunnckoCore"
}
],
"license": "MIT"
}
+14
View File
@@ -0,0 +1,14 @@
/**
* koa-body - index.js
* Copyright(c) 2014
* MIT Licensed
*
* @author Daryl Lau (@dlau)
* @author Charlike Mike Reagent (@tunnckoCore)
* @author Zev Isert (@zevisert)
* @api private
*/
'use strict';
module.exports = Symbol.for('unparsedBody');