forked from midwayjs/midway
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add favicon middleware and remove koa-onerror (midwayjs#1601)
- Loading branch information
1 parent
0669412
commit 2956174
Showing
28 changed files
with
679 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,8 +36,7 @@ | |
"@midwayjs/core": "^3.0.0-beta.17", | ||
"@midwayjs/session": "^3.0.0-beta.17", | ||
"koa": "2.13.4", | ||
"koa-bodyparser": "4.3.0", | ||
"koa-onerror": "^4.1.0" | ||
"koa-bodyparser": "4.3.0" | ||
}, | ||
"author": "Harry Chen <[email protected]>", | ||
"repository": { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,3 +55,8 @@ export const bodyParser = { | |
throw err; | ||
}, | ||
}; | ||
|
||
export const siteFile = { | ||
enable: true, | ||
favicon: undefined, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import * as koaBodyParser from 'koa-bodyparser'; | ||
import { Config, Middleware } from '@midwayjs/decorator'; | ||
|
||
@Middleware() | ||
export class BodyParserMiddleware { | ||
@Config('bodyParser') | ||
bodyparserConfig; | ||
|
||
resolve() { | ||
// use bodyparser middleware | ||
if (this.bodyparserConfig.enable) { | ||
return koaBodyParser(this.bodyparserConfig); | ||
} | ||
} | ||
|
||
static getName() { | ||
return 'bodyParser'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { Config, Middleware } from '@midwayjs/decorator'; | ||
|
||
const path = require('path'); | ||
const MAX_AGE = 'public, max-age=2592000'; // 30 days | ||
|
||
@Middleware() | ||
export class SiteFileMiddleware { | ||
@Config('siteFile') | ||
siteFileConfig; | ||
|
||
resolve() { | ||
// use bodyparser middleware | ||
if (this.siteFileConfig.enable) { | ||
return async (ctx, next) => { | ||
if (ctx.method !== 'HEAD' && ctx.method !== 'GET') return next(); | ||
/* istanbul ignore if */ | ||
if (ctx.path[0] !== '/') return next(); | ||
|
||
if (ctx.path !== '/favicon.ico') { | ||
return next(); | ||
} | ||
|
||
let content = this.siteFileConfig['favicon']; | ||
if (content === undefined) { | ||
content = Buffer.from(''); | ||
} | ||
if (!content) return next(); | ||
// content is url | ||
if (typeof content === 'string') return ctx.redirect(content); | ||
|
||
// '/robots.txt': Buffer <xx.. | ||
// content is buffer | ||
if (Buffer.isBuffer(content)) { | ||
ctx.set('cache-control', MAX_AGE); | ||
ctx.body = content; | ||
ctx.type = path.extname(ctx.path); | ||
return; | ||
} | ||
|
||
return next(); | ||
}; | ||
} | ||
} | ||
|
||
static getName() { | ||
return 'siteFile'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import * as http from 'http'; | ||
import { | ||
accepts, | ||
detectStatus, | ||
isProduction, | ||
sendToWormhole, | ||
escapeHtml, | ||
tpl, | ||
} from './utils'; | ||
|
||
export function setupOnerror(app, config, logger) { | ||
const errorOptions = Object.assign( | ||
{ | ||
// support customize accepts function | ||
accepts() { | ||
const fn = config.accepts || accepts; | ||
return fn(this); | ||
}, | ||
/** | ||
* default text error handler | ||
* @param {Error} err | ||
* @param ctx | ||
*/ | ||
text(err, ctx) { | ||
// unset all headers, and set those specified | ||
ctx.res._headers = {}; | ||
ctx.set(err.headers); | ||
|
||
if (isProduction(app)) { | ||
ctx.body = http.STATUS_CODES[ctx.status]; | ||
} else { | ||
ctx.body = err.message; | ||
} | ||
}, | ||
/** | ||
* default html error handler | ||
* @param {Error} err | ||
*/ | ||
html(err, ctx) { | ||
const status = detectStatus(err); | ||
if (isProduction(app)) { | ||
// 5xx | ||
if (status >= 500) { | ||
ctx.status = 500; | ||
ctx.body = `<h2>Internal Server Error, real status: ${status}</h2>`; | ||
return; | ||
} else { | ||
// 4xx | ||
ctx.status = status; | ||
ctx.body = `<h2>${status} ${http.STATUS_CODES[status]}</h2>`; | ||
return; | ||
} | ||
} | ||
|
||
// show simple error format for unittest | ||
if (app.getEnv() === 'unittest' || app.getEnv() === 'test') { | ||
ctx.status = status; | ||
ctx.body = `${err.name}: ${err.message}\n${err.stack}`; | ||
return; | ||
} | ||
|
||
ctx.body = tpl | ||
.replace('{{status}}', escapeHtml(err.status)) | ||
.replace('{{stack}}', escapeHtml(err.stack)); | ||
ctx.type = 'html'; | ||
}, | ||
/** | ||
* default json error handler | ||
* @param {Error} err | ||
* @param ctx | ||
*/ | ||
json(err, ctx) { | ||
const status = detectStatus(err); | ||
const code = err.code || err.type; | ||
|
||
if (isProduction(app)) { | ||
if (status >= 500) { | ||
ctx.body = { code, message: http.STATUS_CODES[status] }; | ||
} else { | ||
ctx.body = { code, message: err.message }; | ||
} | ||
} else { | ||
ctx.body = { code, message: err.message, stack: err.stack }; | ||
} | ||
}, | ||
}, | ||
config | ||
); | ||
|
||
app.on('error', (err, ctx) => { | ||
ctx = ctx || app.createAnonymousContext(); | ||
const status = detectStatus(err); | ||
// 5xx | ||
if (status >= 500) { | ||
try { | ||
ctx.logger.error(err); | ||
} catch (ex) { | ||
logger.error(err); | ||
logger.error(ex); | ||
} | ||
return; | ||
} | ||
|
||
// 4xx | ||
try { | ||
ctx.logger.warn(err); | ||
} catch (ex) { | ||
logger.warn(err); | ||
logger.error(ex); | ||
} | ||
}); | ||
|
||
app.context.onerror = function (err) { | ||
// don't do anything if there is no error. | ||
// this allows you to pass `this.onerror` | ||
// to node-style callbacks. | ||
if (err == null) return; | ||
|
||
// ignore all pedding request stream | ||
if (this.req) sendToWormhole(this.req); | ||
|
||
// wrap non-error object | ||
if (!(err instanceof Error)) { | ||
const newError = new Error('non-error thrown: ' + err); | ||
// err maybe an object, try to copy the name, message and stack to the new error instance | ||
if (err) { | ||
if (err.name) newError.name = err.name; | ||
if (err.message) newError.message = err.message; | ||
if (err.stack) newError.stack = err.stack; | ||
if (err.status) newError['status'] = err.status; | ||
if (err.headers) newError['headers'] = err.headers; | ||
} | ||
err = newError; | ||
} | ||
|
||
const headerSent = this.headerSent || !this.writable; | ||
if (headerSent) err.headerSent = true; | ||
|
||
// delegate | ||
app.emit('error', err, this); | ||
|
||
// nothing we can do here other | ||
// than delegate to the app-level | ||
// handler and log. | ||
if (headerSent) return; | ||
|
||
// ENOENT support | ||
if (err.code === 'ENOENT') err.status = 404; | ||
|
||
if (typeof err.status !== 'number' || !http.STATUS_CODES[err.status]) { | ||
err.status = 500; | ||
} | ||
this.status = err.status; | ||
|
||
this.set(err.headers); | ||
let type = 'text'; | ||
if (errorOptions.accepts) { | ||
type = errorOptions.accepts.call(this, 'html', 'text', 'json'); | ||
} else { | ||
type = this.accepts('html', 'text', 'json'); | ||
} | ||
type = type || 'text'; | ||
if (errorOptions.all) { | ||
errorOptions.all.call(this, err, this); | ||
} else { | ||
if (errorOptions.redirect && type !== 'json') { | ||
this.redirect(errorOptions.redirect); | ||
} else { | ||
errorOptions[type].call(this, err, this); | ||
this.type = type; | ||
} | ||
} | ||
|
||
if (type === 'json') { | ||
this.body = JSON.stringify(this.body); | ||
} | ||
this.res.end(this.body); | ||
}; | ||
} |
Oops, something went wrong.