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.

132 lines
2.8 KiB

  1. /*!
  2. * method-override
  3. * Copyright(c) 2010 Sencha Inc.
  4. * Copyright(c) 2011 TJ Holowaychuk
  5. * Copyright(c) 2014 Jonathan Ong
  6. * Copyright(c) 2014 Douglas Christopher Wilson
  7. * MIT Licensed
  8. */
  9. 'use strict'
  10. /**
  11. * Module dependencies.
  12. */
  13. var debug = require('debug')('method-override')
  14. var methods = require('methods')
  15. var parseurl = require('parseurl')
  16. var querystring = require('querystring')
  17. var vary = require('vary')
  18. /**
  19. * Method Override:
  20. *
  21. * Provides faux HTTP method support.
  22. *
  23. * Pass an optional `getter` to use when checking for
  24. * a method override.
  25. *
  26. * A string is converted to a getter that will look for
  27. * the method in `req.body[getter]` and a function will be
  28. * called with `req` and expects the method to be returned.
  29. * If the string starts with `X-` then it will look in
  30. * `req.headers[getter]` instead.
  31. *
  32. * The original method is available via `req.originalMethod`.
  33. *
  34. * @param {string|function} [getter=X-HTTP-Method-Override]
  35. * @param {object} [options]
  36. * @return {function}
  37. * @api public
  38. */
  39. module.exports = function methodOverride (getter, options) {
  40. options = options || {}
  41. // get the getter fn
  42. var get = typeof getter === 'function'
  43. ? getter
  44. : createGetter(getter || 'X-HTTP-Method-Override')
  45. // get allowed request methods to examine
  46. var methods = options.methods === undefined
  47. ? ['POST']
  48. : options.methods
  49. return function methodOverride (req, res, next) {
  50. var method
  51. var val
  52. req.originalMethod = req.originalMethod || req.method
  53. // validate request is an allowed method
  54. if (methods && methods.indexOf(req.originalMethod) === -1) {
  55. return next()
  56. }
  57. val = get(req, res)
  58. method = Array.isArray(val)
  59. ? val[0]
  60. : val
  61. // replace
  62. if (method !== undefined && supports(method)) {
  63. req.method = method.toUpperCase()
  64. debug('override %s as %s', req.originalMethod, req.method)
  65. }
  66. next()
  67. }
  68. }
  69. /**
  70. * Create a getter for the given string.
  71. */
  72. function createGetter (str) {
  73. if (str.substr(0, 2).toUpperCase() === 'X-') {
  74. // header getter
  75. return createHeaderGetter(str)
  76. }
  77. return createQueryGetter(str)
  78. }
  79. /**
  80. * Create a getter for the given query key name.
  81. */
  82. function createQueryGetter (key) {
  83. return function (req, res) {
  84. var url = parseurl(req)
  85. var query = querystring.parse(url.query || '')
  86. return query[key]
  87. }
  88. }
  89. /**
  90. * Create a getter for the given header name.
  91. */
  92. function createHeaderGetter (str) {
  93. var header = str.toLowerCase()
  94. return function (req, res) {
  95. // set appropriate Vary header
  96. vary(res, str)
  97. // multiple headers get joined with comma by node.js core
  98. return (req.headers[header] || '').split(/ *, */)
  99. }
  100. }
  101. /**
  102. * Check if node supports `method`.
  103. */
  104. function supports (method) {
  105. return method &&
  106. typeof method === 'string' &&
  107. methods.indexOf(method.toLowerCase()) !== -1
  108. }