/*! * Express - request * Copyright(c) 2010 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var http = require('http') , req = http.IncomingMessage.prototype , utils = require('./utils') , parse = require('url').parse , mime = require('mime'); /** * Default flash formatters. * * @type Object */ var flashFormatters = exports.flashFormatters = { s: function(val){ return String(val); } }; /** * Return request header or optional default. * * The `Referrer` header field is special-cased, * both `Referrer` and `Referer` will yield are * interchangeable. * * Examples: * * req.header('Content-Type'); * // => "text/plain" * * req.header('content-type'); * // => "text/plain" * * req.header('Accept'); * // => undefined * * req.header('Accept', 'text/html'); * // => "text/html" * * @param {String} name * @param {String} defaultValue * @return {String} * @api public */ req.header = function(name, defaultValue){ switch (name = name.toLowerCase()) { case 'referer': case 'referrer': return this.headers.referrer || this.headers.referer || defaultValue; default: return this.headers[name] || defaultValue; } }; /** * Get `field`'s `param` value, defaulting to ''. * * Examples: * * req.get('content-disposition', 'filename'); * // => "something.png" * * @param {String} field * @param {String} param * @return {String} * @api public */ req.get = function(field, param){ var val = this.header(field); if (!val) return ''; var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i'); if (!regexp.exec(val)) return ''; return RegExp.$1 || RegExp.$2; }; /** * Short-hand for `require('url').parse(req.url).pathname`. * * @return {String} * @api public */ req.__defineGetter__('path', function(){ return parse(this.url).pathname; }); /** * Check if the _Accept_ header is present, and includes the given `type`. * * When the _Accept_ header is not present `true` is returned. Otherwise * the given `type` is matched by an exact match, and then subtypes. You * may pass the subtype such as "html" which is then converted internally * to "text/html" using the mime lookup table. * * Examples: * * // Accept: text/html * req.accepts('html'); * // => true * * // Accept: text/*; application/json * req.accepts('html'); * req.accepts('text/html'); * req.accepts('text/plain'); * req.accepts('application/json'); * // => true * * req.accepts('image/png'); * req.accepts('png'); * // => false * * @param {String} type * @return {Boolean} * @api public */ req.accepts = function(type){ var accept = this.header('Accept'); // normalize extensions ".json" -> "json" if (type && '.' == type[0]) type = type.substr(1); // when Accept does not exist, or is '*/*' return true if (!accept || '*/*' == accept) { return true; } else if (type) { // allow "html" vs "text/html" etc if (!~type.indexOf('/')) type = mime.lookup(type); // check if we have a direct match if (~accept.indexOf(type)) return true; // check if we have type/* type = type.split('/')[0] + '/*'; return !!~accept.indexOf(type); } else { return false; } }; /** * Return the value of param `name` when present or `defaultValue`. * * - Checks route placeholders, ex: _/user/:id_ * - Checks query string params, ex: ?id=12 * - Checks urlencoded body params, ex: id=12 * * To utilize urlencoded request bodies, `req.body` * should be an object. This can be done by using * the `connect.bodyParser` middleware. * * @param {String} name * @param {Mixed} defaultValue * @return {String} * @api public */ req.param = function(name, defaultValue){ // route params like /user/:id if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) { return this.params[name]; } // query string params if (undefined !== this.query[name]) { return this.query[name]; } // request body params via connect.bodyParser if (this.body && undefined !== this.body[name]) { return this.body[name]; } return defaultValue; }; /** * Queue flash `msg` of the given `type`. * * Examples: * * req.flash('info', 'email sent'); * req.flash('error', 'email delivery failed'); * req.flash('info', 'email re-sent'); * // => 2 * * req.flash('info'); * // => ['email sent', 'email re-sent'] * * req.flash('info'); * // => [] * * req.flash(); * // => { error: ['email delivery failed'], info: [] } * * Formatting: * * Flash notifications also support arbitrary formatting support. * For example you may pass variable arguments to `req.flash()` * and use the %s specifier to be replaced by the associated argument: * * req.flash('info', 'email has been sent to %s.', userName); * * To add custom formatters use the `exports.flashFormatters` object. * * @param {String} type * @param {String} msg * @return {Array|Object|Number} * @api public */ req.flash = function(type, msg){ if (this.session === undefined) throw Error('req.flash() requires sessions'); var msgs = this.session.flash = this.session.flash || {}; if (type && msg) { var i = 2 , args = arguments , formatters = this.app.flashFormatters || {}; formatters.__proto__ = flashFormatters; msg = utils.miniMarkdown(msg); msg = msg.replace(/%([a-zA-Z])/g, function(_, format){ var formatter = formatters[format]; if (formatter) return formatter(utils.escape(args[i++])); }); return (msgs[type] = msgs[type] || []).push(msg); } else if (type) { var arr = msgs[type]; delete msgs[type]; return arr || []; } else { this.session.flash = {}; return msgs; } }; /** * Check if the incoming request contains the "Content-Type" * header field, and it contains the give mime `type`. * * Examples: * * // With Content-Type: text/html; charset=utf-8 * req.is('html'); * req.is('text/html'); * // => true * * // When Content-Type is application/json * req.is('json'); * req.is('application/json'); * // => true * * req.is('html'); * // => false * * Ad-hoc callbacks can also be registered with Express, to perform * assertions again the request, for example if we need an expressive * way to check if our incoming request is an image, we can register "an image" * callback: * * app.is('an image', function(req){ * return 0 == req.headers['content-type'].indexOf('image'); * }); * * Now within our route callbacks, we can use to to assert content types * such as "image/jpeg", "image/png", etc. * * app.post('/image/upload', function(req, res, next){ * if (req.is('an image')) { * // do something * } else { * next(); * } * }); * * @param {String} type * @return {Boolean} * @api public */ req.is = function(type){ var fn = this.app.is(type); if (fn) return fn(this); var ct = this.headers['content-type']; if (!ct) return false; ct = ct.split(';')[0]; if (!~type.indexOf('/')) type = mime.lookup(type); if (~type.indexOf('*')) { type = type.split('/'); ct = ct.split('/'); if ('*' == type[0] && type[1] == ct[1]) return true; if ('*' == type[1] && type[0] == ct[0]) return true; return false; } return !! ~ct.indexOf(type); }; // Callback for isXMLHttpRequest / xhr function isxhr() { return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest'; } /** * Check if the request was an _XMLHttpRequest_. * * @return {Boolean} * @api public */ req.__defineGetter__('isXMLHttpRequest', isxhr); req.__defineGetter__('xhr', isxhr);