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.

160 lines
3.9 KiB

8 years ago
  1. var Negotiator = require('negotiator')
  2. var mime = require('mime-types')
  3. var slice = [].slice
  4. module.exports = Accepts
  5. function Accepts(req) {
  6. if (!(this instanceof Accepts))
  7. return new Accepts(req)
  8. this.headers = req.headers
  9. this.negotiator = Negotiator(req)
  10. }
  11. /**
  12. * Check if the given `type(s)` is acceptable, returning
  13. * the best match when true, otherwise `undefined`, in which
  14. * case you should respond with 406 "Not Acceptable".
  15. *
  16. * The `type` value may be a single mime type string
  17. * such as "application/json", the extension name
  18. * such as "json" or an array `["json", "html", "text/plain"]`. When a list
  19. * or array is given the _best_ match, if any is returned.
  20. *
  21. * Examples:
  22. *
  23. * // Accept: text/html
  24. * this.types('html');
  25. * // => "html"
  26. *
  27. * // Accept: text/*, application/json
  28. * this.types('html');
  29. * // => "html"
  30. * this.types('text/html');
  31. * // => "text/html"
  32. * this.types('json', 'text');
  33. * // => "json"
  34. * this.types('application/json');
  35. * // => "application/json"
  36. *
  37. * // Accept: text/*, application/json
  38. * this.types('image/png');
  39. * this.types('png');
  40. * // => undefined
  41. *
  42. * // Accept: text/*;q=.5, application/json
  43. * this.types(['html', 'json']);
  44. * this.types('html', 'json');
  45. * // => "json"
  46. *
  47. * @param {String|Array} type(s)...
  48. * @return {String|Array|Boolean}
  49. * @api public
  50. */
  51. Accepts.prototype.type =
  52. Accepts.prototype.types = function (types) {
  53. if (!Array.isArray(types)) types = slice.call(arguments);
  54. var n = this.negotiator;
  55. if (!types.length) return n.mediaTypes();
  56. if (!this.headers.accept) return types[0];
  57. var mimes = types.map(extToMime);
  58. var accepts = n.mediaTypes(mimes.filter(validMime));
  59. var first = accepts[0];
  60. if (!first) return false;
  61. return types[mimes.indexOf(first)];
  62. }
  63. /**
  64. * Return accepted encodings or best fit based on `encodings`.
  65. *
  66. * Given `Accept-Encoding: gzip, deflate`
  67. * an array sorted by quality is returned:
  68. *
  69. * ['gzip', 'deflate']
  70. *
  71. * @param {String|Array} encoding(s)...
  72. * @return {String|Array}
  73. * @api public
  74. */
  75. Accepts.prototype.encoding =
  76. Accepts.prototype.encodings = function (encodings) {
  77. if (!Array.isArray(encodings)) encodings = slice.call(arguments);
  78. var n = this.negotiator;
  79. if (!encodings.length) return n.encodings();
  80. return n.encodings(encodings)[0] || false;
  81. }
  82. /**
  83. * Return accepted charsets or best fit based on `charsets`.
  84. *
  85. * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
  86. * an array sorted by quality is returned:
  87. *
  88. * ['utf-8', 'utf-7', 'iso-8859-1']
  89. *
  90. * @param {String|Array} charset(s)...
  91. * @return {String|Array}
  92. * @api public
  93. */
  94. Accepts.prototype.charset =
  95. Accepts.prototype.charsets = function (charsets) {
  96. if (!Array.isArray(charsets)) charsets = [].slice.call(arguments);
  97. var n = this.negotiator;
  98. if (!charsets.length) return n.charsets();
  99. if (!this.headers['accept-charset']) return charsets[0];
  100. return n.charsets(charsets)[0] || false;
  101. }
  102. /**
  103. * Return accepted languages or best fit based on `langs`.
  104. *
  105. * Given `Accept-Language: en;q=0.8, es, pt`
  106. * an array sorted by quality is returned:
  107. *
  108. * ['es', 'pt', 'en']
  109. *
  110. * @param {String|Array} lang(s)...
  111. * @return {Array|String}
  112. * @api public
  113. */
  114. Accepts.prototype.lang =
  115. Accepts.prototype.langs =
  116. Accepts.prototype.language =
  117. Accepts.prototype.languages = function (langs) {
  118. if (!Array.isArray(langs)) langs = slice.call(arguments);
  119. var n = this.negotiator;
  120. if (!langs.length) return n.languages();
  121. if (!this.headers['accept-language']) return langs[0];
  122. return n.languages(langs)[0] || false;
  123. }
  124. /**
  125. * Convert extnames to mime.
  126. *
  127. * @param {String} type
  128. * @return {String}
  129. * @api private
  130. */
  131. function extToMime(type) {
  132. if (~type.indexOf('/')) return type;
  133. return mime.lookup(type);
  134. }
  135. /**
  136. * Check if mime is valid.
  137. *
  138. * @param {String} type
  139. * @return {String}
  140. * @api private
  141. */
  142. function validMime(type) {
  143. return typeof type === 'string';
  144. }