json.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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]*([^\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. message: e.message,
  77. stack: e.stack
  78. })
  79. }
  80. }
  81. return function jsonParser (req, res, next) {
  82. if (req._body) {
  83. debug('body already parsed')
  84. next()
  85. return
  86. }
  87. req.body = req.body || {}
  88. // skip requests without bodies
  89. if (!typeis.hasBody(req)) {
  90. debug('skip empty body')
  91. next()
  92. return
  93. }
  94. debug('content-type %j', req.headers['content-type'])
  95. // determine if request should be parsed
  96. if (!shouldParse(req)) {
  97. debug('skip parsing')
  98. next()
  99. return
  100. }
  101. // assert charset per RFC 7159 sec 8.1
  102. var charset = getCharset(req) || 'utf-8'
  103. if (charset.slice(0, 4) !== 'utf-') {
  104. debug('invalid charset')
  105. next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
  106. charset: charset,
  107. type: 'charset.unsupported'
  108. }))
  109. return
  110. }
  111. // read
  112. read(req, res, next, parse, debug, {
  113. encoding: charset,
  114. inflate: inflate,
  115. limit: limit,
  116. verify: verify
  117. })
  118. }
  119. }
  120. /**
  121. * Create strict violation syntax error matching native error.
  122. *
  123. * @param {string} str
  124. * @param {string} char
  125. * @return {Error}
  126. * @private
  127. */
  128. function createStrictSyntaxError (str, char) {
  129. var index = str.indexOf(char)
  130. var partial = index !== -1
  131. ? str.substring(0, index) + '#'
  132. : ''
  133. try {
  134. JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
  135. } catch (e) {
  136. return normalizeJsonSyntaxError(e, {
  137. message: e.message.replace('#', char),
  138. stack: e.stack
  139. })
  140. }
  141. }
  142. /**
  143. * Get the first non-whitespace character in a string.
  144. *
  145. * @param {string} str
  146. * @return {function}
  147. * @private
  148. */
  149. function firstchar (str) {
  150. var match = FIRST_CHAR_REGEXP.exec(str)
  151. return match
  152. ? match[1]
  153. : undefined
  154. }
  155. /**
  156. * Get the charset of a request.
  157. *
  158. * @param {object} req
  159. * @api private
  160. */
  161. function getCharset (req) {
  162. try {
  163. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  164. } catch (e) {
  165. return undefined
  166. }
  167. }
  168. /**
  169. * Normalize a SyntaxError for JSON.parse.
  170. *
  171. * @param {SyntaxError} error
  172. * @param {object} obj
  173. * @return {SyntaxError}
  174. */
  175. function normalizeJsonSyntaxError (error, obj) {
  176. var keys = Object.getOwnPropertyNames(error)
  177. for (var i = 0; i < keys.length; i++) {
  178. var key = keys[i]
  179. if (key !== 'stack' && key !== 'message') {
  180. delete error[key]
  181. }
  182. }
  183. // replace stack before message for Node.js 0.10 and below
  184. error.stack = obj.stack.replace(error.message, obj.message)
  185. error.message = obj.message
  186. return error
  187. }
  188. /**
  189. * Get the simple type checker.
  190. *
  191. * @param {string} type
  192. * @return {function}
  193. */
  194. function typeChecker (type) {
  195. return function checkType (req) {
  196. return Boolean(typeis(req, type))
  197. }
  198. }