优化中间件/路由/跨域

v1
宇天 2020-09-22 19:58:29 +08:00
parent b2c424a384
commit 38ad516e08
4 changed files with 80 additions and 87 deletions

View File

@ -36,9 +36,8 @@ function hideProperty(host, name, value) {
export default class Five { export default class Five {
constructor() { constructor() {
hideProperty(this, '__FIVE__', Object.assign({}, init)) hideProperty(this, '__FIVE__', Object.assign({}, init))
hideProperty(this, '__MODULES__', { __error__: null }) hideProperty(this, '__MODULES__', {})
hideProperty(this, '__MIDDLEWARE__', []) hideProperty(this, '__MIDDLEWARE__', [])
hideProperty(this, '__INSTANCE__', {})
} }
__init__() { __init__() {
@ -94,63 +93,51 @@ export default class Five {
// 获取全局配置 // 获取全局配置
get(key) { get(key) {
try { return this.__FIVE__[key]
return new Function('o', `return o.${key}`)(this.__FIVE__)
} catch (err) {}
} }
// 加载中间件/缓存模块 // 加载中间件
// 与别的中间件用法有些不一样, 回调的传入参数中的req和res, // 与别的中间件用法有些不一样, 回调的传入参数中的req和res,
// 并非原生的request对象和response对象, // 并非原生的request对象和response对象,
// 而是框架内部封装过的,可通过origin属性访问原生的对象 // 而是框架内部封装过的,可通过origin属性访问原生的对象
use(key, fn) { use(fn) {
if (arguments.length === 1) { if (typeof fn === 'function') {
if (typeof key !== 'function') { this.__MIDDLEWARE__.push(fn)
throw TypeError('argument 1 must be a callback') return this
}
this.__MIDDLEWARE__.push(key)
} else {
if (typeof key !== 'string') {
return
}
libs[key] = fn
} }
throw TypeError('argument must be a callback')
} }
// 预加载应用
// 预加载应用, 缓存以提高性能
preload(dir) { preload(dir) {
var list = fs.ls(dir) var list = fs.ls(dir)
if (list) { if (list) {
list.forEach(file => { list.forEach(item => {
var { name } = path.parse(file) var { name } = path.parse(item)
if (name.startsWith('.')) { if (name.startsWith('.')) {
return return
} }
try { // 如果是目录,则默认加载index.js, 其他文件不加载
this.__MODULES__[name] = import(file) // 交由index.js自行处理, 用于复杂的应用
} catch (err) { if (fs.isdir(item)) {
this.__MODULES__.__error__ = err item = path.join(item, './index.js')
} }
this.__MODULES__[name] = import(item).catch(err => {
return { default: null }
})
}) })
} }
return this return this
} }
// 注实例化对象到实例池中 // 注实例化对象到实例池中
// 与use方法不同的是, 这个会在server创建之前就已经执行 // 与use方法不同的是, 这个会在server创建之前就已经执行
ins(name, fn) { install({ name, install }) {
var _this = this this['$$' + name] = install.call(this, this.__FIVE__)
if (arguments.length === 1) { return this
return this.__INSTANCE__[name]
}
if (typeof fn === 'function') {
fn.call(this, this.__FIVE__, function next(instance) {
if (instance) {
_this.__INSTANCE__[name] = instance
}
})
}
} }
// 启动http服务 // 启动http服务

View File

@ -1,11 +1,11 @@
/** /**
* * 部分配置
* @authors yutent (yutent@doui.cc) * @author yutent<yutent.io@gmail.com>
* @date 2016-05-15 14:37:47 * @date 2020/09/22 17:19:39
*
*/ */
'use strict' const ENV_PROD = 'production'
const ENV_DEV = 'development'
export default { export default {
db: {}, db: {},
@ -24,27 +24,30 @@ export default {
domain: '', // cookie域, 默认等于website domain: '', // cookie域, 默认等于website
port: 3000, port: 3000,
routeMode: 'action', // action | __main__ routeMode: 'action', // action | __main__
env: process.env.NODE_ENV === 'production' ? 'production' : 'development', env: process.env.NODE_ENV === ENV_PROD ? ENV_PROD : ENV_DEV,
debug: process.env.NODE_ENV !== 'production', // debug模式 debug: process.env.NODE_ENV === ENV_DEV, // debug模式
smtp: { smtp: {
host: 'smtp.example.com', host: 'smtp.example.com',
port: 25, port: 25,
mail: 'service@example.com', mail: 'no-reply@example.com',
name: '系统邮件', name: 'no-reply',
passwd: '' passwd: ''
}, },
supportCredentials: false, cors: {
credentialsRule: null, enabled: false,
credentialsMaxAge: 0, credentials: false,
origin: [], // ['abc.com', 'a.foo.com']
maxAge: 0
},
jwt: null, // jwt secret jwt: null, // jwt secret
regexp: { regexp: {
// 常用正则 // 常用正则
email: /^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/, email: /^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/,
uname: /^[A-Za-z\d_]{4,16}$/, uname: /^[A-Za-z\d_]{4,16}$/,
passwd: /^[\S]{6,20}$/, passwd: /^\S{6,20}$/,
phone: /^1[34578]\d{9}$/, phone: /^1[3456789]\d{9}$/,
idCard: /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/, idCard: /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/,
CN: /^[\u4e00-\u9fa5]+$/, cn: /^[\u4e00-\u9fa5]+$/,
qq: /^\d{5,12}$/ qq: /^\d{5,12}$/
} }
} }

View File

@ -7,28 +7,29 @@
import url from 'url' import url from 'url'
export default function(req, res, next) { export default function(req, res, next) {
var supportCredentials = this.get('supportCredentials') var CORS = this.get('cors')
var credentialsRule = this.get('credentialsRule')
var credentialsMaxAge = this.get('credentialsMaxAge')
if (supportCredentials) { if (CORS.enabled) {
var origin = req.header('origin') || req.header('referer') || '' var origin = req.header('origin') || req.header('referer') || ''
var headers = req.header('access-control-request-headers') var headers = req.header('access-control-request-headers')
origin = url.parse(origin) var { hostname, host, protocol } = url.parse(origin)
if (credentialsRule && origin.hostname) { if (CORS.origin.length && hostname) {
if (!credentialsRule.test(origin.hostname)) { if (!CORS.origin.includes(hostname)) {
return res.end('') return res.end('')
} }
} }
res.set('Access-Control-Allow-Credentials', 'true') res.set('Access-Control-Allow-Credentials', 'true')
res.set('Access-Control-Allow-Origin', `${origin.protocol}//${origin.host}`) res.set('Access-Control-Allow-Origin', `${protocol}//${host}`)
if (headers) { if (headers) {
res.set('Access-Control-Allow-Headers', headers) res.set('Access-Control-Allow-Headers', headers)
} }
if (credentialsMaxAge) {
res.set('Access-Control-Max-Age', credentialsMaxAge) if (CORS.maxAge) {
res.set('Access-Control-Max-Age', CORS.maxAge)
} }
if (req.method === 'OPTIONS') { if (req.method === 'OPTIONS') {
return res.end('') return res.end('')
} }

View File

@ -6,18 +6,14 @@
export default function(req, res, next) { export default function(req, res, next) {
var debug = this.get('debug') var debug = this.get('debug')
if (this.__MODULES__.__error__) {
var err = this.__MODULES__.__error__
return res.error(debug ? err.stack || err : err, err.status || 500)
}
// 1. 先判断控制器是否存在 // 1. 先判断控制器是否存在
if (!this.__MODULES__[req.app]) { if (!this.__MODULES__[req.app]) {
if (this.__MODULES__.__error__) { return res.error(`Controller [${req.app}] not found`, 404)
res.error(
debug ? this.__MODULES__.__error__.stack : this.__MODULES__.__error__,
500
)
} else {
res.error(`Controller [${req.app}] not found`, 404)
}
return
} }
// 2. 默认二级路由为index // 2. 默认二级路由为index
@ -28,29 +24,35 @@ export default function(req, res, next) {
// 3. 实例化控制器 // 3. 实例化控制器
this.__MODULES__[req.app] this.__MODULES__[req.app]
.then(({ default: Mod }) => { .then(({ default: Mod }) => {
var app = new Mod({ ctx: this, req, res }) var app,
var err = '' err = ''
if (Mod) {
app = new Mod({ ctx: this, req, res })
// action模式, 则路由自动调用对应的action方法 // action模式, 则路由自动调用对应的action方法
// __main__模式, 则路由全部走__main__方法 // __main__模式, 则路由全部走__main__方法
if (this.get('routeMode') === 'action') { if (this.get('routeMode') === 'action') {
var route = req.path.shift() var route = req.path.shift()
var act = route + 'Action' var act = route + 'Action'
if (app[act]) { if (app[act]) {
return app[act].apply(app, req.path) return app[act].apply(app, req.path)
} else {
err = new Error(`Route [${route}] not found`)
}
} else { } else {
err = new Error(`Route [${route}] not found`) if (app.__main__) {
return app.__main__.apply(app, req.path)
} else {
err = new Error('__main__() not found')
}
} }
err.status = 404
} else { } else {
if (app.__main__) { err = new Error(`Controller [${req.app}] load error`)
return app.__main__.apply(app, req.path) err.status = 500
} else {
err = new Error('__main__() not found')
}
} }
err.status = 404
return Promise.reject(err) return Promise.reject(err)
}) })
.catch(err => { .catch(err => {