You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

json.js 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. 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.substr(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 = str.substring(0, index) + '#'
  131. try {
  132. JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
  133. } catch (e) {
  134. return normalizeJsonSyntaxError(e, {
  135. message: e.message.replace('#', char),
  136. stack: e.stack
  137. })
  138. }
  139. }
  140. /**
  141. * Get the first non-whitespace character in a string.
  142. *
  143. * @param {string} str
  144. * @return {function}
  145. * @private
  146. */
  147. function firstchar (str) {
  148. return FIRST_CHAR_REGEXP.exec(str)[1]
  149. }
  150. /**
  151. * Get the charset of a request.
  152. *
  153. * @param {object} req
  154. * @api private
  155. */
  156. function getCharset (req) {
  157. try {
  158. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  159. } catch (e) {
  160. return undefined
  161. }
  162. }
  163. /**
  164. * Normalize a SyntaxError for JSON.parse.
  165. *
  166. * @param {SyntaxError} error
  167. * @param {object} obj
  168. * @return {SyntaxError}
  169. */
  170. function normalizeJsonSyntaxError (error, obj) {
  171. var keys = Object.getOwnPropertyNames(error)
  172. for (var i = 0; i < keys.length; i++) {
  173. var key = keys[i]
  174. if (key !== 'stack' && key !== 'message') {
  175. delete error[key]
  176. }
  177. }
  178. // replace stack before message for Node.js 0.10 and below
  179. error.stack = obj.stack.replace(error.message, obj.message)
  180. error.message = obj.message
  181. return error
  182. }
  183. /**
  184. * Get the simple type checker.
  185. *
  186. * @param {string} type
  187. * @return {function}
  188. */
  189. function typeChecker (type) {
  190. return function checkType (req) {
  191. return Boolean(typeis(req, type))
  192. }
  193. }