json.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*!
  2. * body-parser
  3. * Copyright(c) 2014 Jonathan Ong
  4. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module dependencies.
  10. * @private
  11. */
  12. var bytes = require('bytes')
  13. var contentType = require('content-type')
  14. var createError = require('http-errors')
  15. var debug = require('debug')('body-parser:json')
  16. var read = require('../read')
  17. var typeis = require('type-is')
  18. /**
  19. * Module exports.
  20. */
  21. module.exports = json
  22. /**
  23. * RegExp to match the first non-space in a string.
  24. *
  25. * Allowed whitespace is defined in RFC 7159:
  26. *
  27. * ws = *(
  28. * %x20 / ; Space
  29. * %x09 / ; Horizontal tab
  30. * %x0A / ; Line feed or New line
  31. * %x0D ) ; Carriage return
  32. */
  33. var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*(.)/ // eslint-disable-line no-control-regex
  34. /**
  35. * Create a middleware to parse JSON bodies.
  36. *
  37. * @param {object} [options]
  38. * @return {function}
  39. * @public
  40. */
  41. function json (options) {
  42. var opts = options || {}
  43. var limit = typeof opts.limit !== 'number'
  44. ? bytes.parse(opts.limit || '100kb')
  45. : opts.limit
  46. var inflate = opts.inflate !== false
  47. var reviver = opts.reviver
  48. var strict = opts.strict !== false
  49. var type = opts.type || 'application/json'
  50. var verify = opts.verify || false
  51. if (verify !== false && typeof verify !== 'function') {
  52. throw new TypeError('option verify must be function')
  53. }
  54. // create the appropriate type checking function
  55. var shouldParse = typeof type !== 'function'
  56. ? typeChecker(type)
  57. : type
  58. function parse (body) {
  59. if (body.length === 0) {
  60. // special-case empty json body, as it's a common client-side mistake
  61. // TODO: maybe make this configurable or part of "strict" option
  62. return {}
  63. }
  64. if (strict) {
  65. var first = firstchar(body)
  66. if (first !== '{' && first !== '[') {
  67. debug('strict violation')
  68. throw createStrictSyntaxError(body, first)
  69. }
  70. }
  71. try {
  72. debug('parse json')
  73. return JSON.parse(body, reviver)
  74. } catch (e) {
  75. throw normalizeJsonSyntaxError(e, {
  76. stack: e.stack
  77. })
  78. }
  79. }
  80. return function jsonParser (req, res, next) {
  81. if (req._body) {
  82. debug('body already parsed')
  83. next()
  84. return
  85. }
  86. req.body = req.body || {}
  87. // skip requests without bodies
  88. if (!typeis.hasBody(req)) {
  89. debug('skip empty body')
  90. next()
  91. return
  92. }
  93. debug('content-type %j', req.headers['content-type'])
  94. // determine if request should be parsed
  95. if (!shouldParse(req)) {
  96. debug('skip parsing')
  97. next()
  98. return
  99. }
  100. // assert charset per RFC 7159 sec 8.1
  101. var charset = getCharset(req) || 'utf-8'
  102. if (charset.substr(0, 4) !== 'utf-') {
  103. debug('invalid charset')
  104. next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
  105. charset: charset,
  106. type: 'charset.unsupported'
  107. }))
  108. return
  109. }
  110. // read
  111. read(req, res, next, parse, debug, {
  112. encoding: charset,
  113. inflate: inflate,
  114. limit: limit,
  115. verify: verify
  116. })
  117. }
  118. }
  119. /**
  120. * Create strict violation syntax error matching native error.
  121. *
  122. * @param {string} str
  123. * @param {string} char
  124. * @return {Error}
  125. * @private
  126. */
  127. function createStrictSyntaxError (str, char) {
  128. var index = str.indexOf(char)
  129. var partial = str.substring(0, index) + '#'
  130. try {
  131. JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
  132. } catch (e) {
  133. return normalizeJsonSyntaxError(e, {
  134. message: e.message.replace('#', char),
  135. stack: e.stack
  136. })
  137. }
  138. }
  139. /**
  140. * Get the first non-whitespace character in a string.
  141. *
  142. * @param {string} str
  143. * @return {function}
  144. * @private
  145. */
  146. function firstchar (str) {
  147. return FIRST_CHAR_REGEXP.exec(str)[1]
  148. }
  149. /**
  150. * Get the charset of a request.
  151. *
  152. * @param {object} req
  153. * @api private
  154. */
  155. function getCharset (req) {
  156. try {
  157. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  158. } catch (e) {
  159. return undefined
  160. }
  161. }
  162. /**
  163. * Normalize a SyntaxError for JSON.parse.
  164. *
  165. * @param {SyntaxError} error
  166. * @param {object} obj
  167. * @return {SyntaxError}
  168. */
  169. function normalizeJsonSyntaxError (error, obj) {
  170. var keys = Object.getOwnPropertyNames(error)
  171. for (var i = 0; i < keys.length; i++) {
  172. var key = keys[i]
  173. if (key !== 'stack' && key !== 'message') {
  174. delete error[key]
  175. }
  176. }
  177. var props = Object.keys(obj)
  178. for (var j = 0; j < props.length; j++) {
  179. var prop = props[j]
  180. error[prop] = obj[prop]
  181. }
  182. return error
  183. }
  184. /**
  185. * Get the simple type checker.
  186. *
  187. * @param {string} type
  188. * @return {function}
  189. */
  190. function typeChecker (type) {
  191. return function checkType (req) {
  192. return Boolean(typeis(req, type))
  193. }
  194. }