thinkjs
Version:
ThinkJS - Use full ES6+ features to develop web applications
321 lines (316 loc) • 8.12 kB
JavaScript
const helper = require('think-helper');
const assert = require('assert');
const Cookies = require('cookies');
const {URL} = require('url');
const PARAM = Symbol('context-param');
const POST = Symbol('context-post');
const FILE = Symbol('context-file');
const COOKIE_STORE = Symbol('cookie-store');
const path = require('path');
const fs = require('fs');
const onFinished = require('on-finished');
const destroy = require('destroy');
/**
* extend context
*/
module.exports = {
/**
* get userAgent header
*/
get userAgent() {
return this.header['user-agent'];
},
/**
* is get request
*/
get isGet() {
return this.method === 'GET';
},
/**
* is post request
*/
get isPost() {
return this.method === 'POST';
},
/**
* is command line invoke
*/
get isCli() {
return this.method === 'CLI';
},
/**
* get referer header
*/
referer(onlyHost) {
return this.referrer(onlyHost);
},
/**
* get referer header
*/
referrer(onlyHost) {
const referrer = this.header['referer'];
if (!referrer || !onlyHost) return referrer;
try {
return new URL(referrer).hostname;
} catch (e) {}
},
/**
* is method
*/
isMethod(method) {
return this.method === method.toUpperCase();
},
/**
* is ajax request
*/
isAjax(method) {
if (method && !this.isMethod(method)) return false;
return this.header['x-requested-with'] === 'XMLHttpRequest';
},
/**
* is jsonp request
*/
isJsonp(callbackField = this.config('jsonpCallbackField')) {
return !!this.param(callbackField);
},
/**
* send jsonp data
*/
jsonp(data, callbackField = this.config('jsonpCallbackField')) {
let field = this.param(callbackField);
// remove unsafe chars
field = (field || '').replace(/[^\w.]/g, '');
if (field) {
data = `${field}(${JSON.stringify(data)})`;
}
this.type = this.config('jsonpContentType');
this.body = data;
return false;
},
/**
* send json data
*/
json(data) {
this.type = this.config('jsonContentType');
this.body = data;
return false;
},
/**
* send success data
*/
success(data = '', message = '') {
const obj = {
[this.config('errnoField')]: 0,
[this.config('errmsgField')]: message,
data
};
this.type = this.config('jsonContentType');
this.body = obj;
return false;
},
/**
* send fail data
*/
fail(errno, errmsg = '', data = '') {
let obj;
if (helper.isObject(errno)) {
obj = errno;
} else {
if (/^[A-Z_]+$/.test(errno)) {
const messages = think.app.validators.messages || {};
const msg = messages[errno];
if (think.isArray(msg)) {
[errno, errmsg] = msg;
}
}
if (!think.isNumber(errno)) {
[data, errmsg, errno] = [errmsg, errno, this.config('defaultErrno')];
}
obj = {
[this.config('errnoField')]: errno,
[this.config('errmsgField')]: errmsg
};
if (data) {
obj.data = data;
}
}
this.type = this.config('jsonContentType');
this.body = obj;
return false;
},
/**
* set expires header
*/
expires(time) {
time = helper.ms(time);
const date = new Date(Date.now() + time);
this.set('Cache-Control', `max-age=${time}`);
this.set('Expires', date.toUTCString());
},
/**
* get or set configs
* @param {String} name
* @param {Mixed} value
* @param {String} m
*/
config(name, value, m = this.module) {
return think.config(name, value, m);
},
/**
* get or set query data
* `query` or `get` is already used in koa
* @param {String} name
* @param {Mixed} value
*/
param(name, value) {
if (!this[PARAM]) {
this[PARAM] = Object.assign({}, this.request._query || this.request.query);
this.app.emit('filterParam', this[PARAM]);
}
if (!name) return this[PARAM];
if (helper.isObject(name)) {
this[PARAM] = Object.assign(this[PARAM], name);
return this;
}
if (value === undefined) {
// this.param('a,b')
if (helper.isString(name) && name.indexOf(',') > -1) {
name = name.split(/\s*,\s*/);
}
if (helper.isArray(name)) {
const value = {};
name.forEach(item => {
const val = this[PARAM][item];
if (val !== undefined) {
value[item] = val;
}
});
return value;
}
return this[PARAM][name];
}
this[PARAM][name] = value;
return this;
},
/**
* get or set post data
* @param {String} name
* @param {Mixed} value
*/
post(name, value) {
if (!this[POST]) {
const json = this.request.body && this.request.body.post;
this[POST] = think.isArray(json) ? Array.from(json) : Object.assign({}, json);
this.app.emit('filterParam', this[POST]);
}
if (!name) return this[POST];
if (helper.isObject(name)) {
this[POST] = Object.assign(this[POST], name);
return this;
}
if (value === undefined) {
// this.param('a,b')
if (helper.isString(name) && name.indexOf(',') > -1) {
name = name.split(/\s*,\s*/);
}
if (helper.isArray(name)) {
const value = {};
name.forEach(item => {
const val = this[POST][item];
if (val !== undefined) {
value[item] = val;
}
});
return value;
}
return this[POST][name];
}
this[POST][name] = value;
return this;
},
/**
* get or set file data
* @param {String} name
* @param {Mixed} value
*/
file(name, value) {
if (!this[FILE]) {
this[FILE] = Object.assign({}, this.request.body && this.request.body.file);
}
if (!name) return this[FILE];
if (helper.isObject(name)) {
this[FILE] = Object.assign(this[FILE], name);
return this;
}
if (value === undefined) {
return this[FILE][name];
}
this[FILE][name] = value;
return this;
},
/**
* get or set cookie
* @param {String} name
* @param {String} value
* @param {Object} options
*/
cookie(name, value, options = {}) {
assert(name && helper.isString(name), 'cookie.name must be a string');
options = Object.assign({}, this.config('cookie'), options);
const instance = new Cookies(this.req, this.res, {
keys: options.keys,
secure: this.request.secure
});
if (!this[COOKIE_STORE]) this[COOKIE_STORE] = {};
// get cookie
if (value === undefined) {
if (this[COOKIE_STORE][name] !== undefined) return this[COOKIE_STORE][name];
return instance.get(name, options);
}
// remove cookie
if (value === null) {
delete this[COOKIE_STORE][name];
// If the value is omitted, an outbound header with an expired date is used to delete the cookie.
// https://github.com/pillarjs/cookies#cookiesset-name--value---options--
return instance.set(name, undefined, options);
}
assert(helper.isString(value), 'cookie value must be a string');
// http://browsercookielimits.squawky.net/
if (value.length >= 4094) {
this.app.emit('cookieLimit', {name, value, ctx: this});
}
this[COOKIE_STORE][name] = value;
// set cookie
return instance.set(name, value, options);
},
/**
* get service
* @param {String} name
* @param {String} m
*/
service(...args) {
return think.service(...args);
},
/**
* download
* @param {String} filepath
* @param {String} filename
*/
download(filepath, filename = path.basename(filepath)) {
assert(filepath, 'filepath can not be empty');
const contentType = this.response.get('Content-Type');
if (!contentType) {
this.type = path.extname(filename);
}
const contentDisposition = this.response.get('Content-Disposition');
if (!contentDisposition) {
this.attachment(filename);
}
const stream = fs.createReadStream(filepath);
this.body = stream;
onFinished(this.res, () => {
destroy(stream);
});
return false;
}
};