diff --git a/app/middleware/notfound.js b/app/middleware/notfound.js index f97562cd4e..daaf28705b 100644 --- a/app/middleware/notfound.js +++ b/app/middleware/notfound.js @@ -18,16 +18,19 @@ module.exports = options => { return; } - if (options.enableRedirect && options.pageUrl) { + const notFoundHtml = '

404 Not Found

'; + + // notfound handler is unimplemented + if (options.pageUrl && this.path === options.pageUrl) { + this.body = `${notFoundHtml}

config.notfound.pageUrl(${options.pageUrl})
is unimplemented

`; + return; + } + + if (options.pageUrl) { this.realStatus = 404; this.redirect(options.pageUrl); return; } - const title = '

404 Not Found

'; - if (!options.enableRedirect && options.pageUrl) { - this.body = `${title}Because you are in a non-prod environment, you will be looking at this page, otherwise it will jump to ${options.pageUrl}`; - } else { - this.body = title; - } + this.body = notFoundHtml; }; }; diff --git a/config/config.default.js b/config/config.default.js index dc22505bed..254d766cd1 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -106,7 +106,6 @@ module.exports = appInfo => { */ exports.notfound = { pageUrl: '', - enableRedirect: appInfo.env === 'prod', }; /** diff --git a/docs/source/zh-cn/core/error-handling.md b/docs/source/zh-cn/core/error-handling.md index 96161646ee..0e096456b7 100644 --- a/docs/source/zh-cn/core/error-handling.md +++ b/docs/source/zh-cn/core/error-handling.md @@ -52,15 +52,15 @@ this.runInBackground(function* () { | 请求需求的格式 | 环境 | errorPageUrl 是否配置 | 返回内容 | |-------------|------|----------------------|--------| -| html & text | local & unittest | - | onerror 自带的错误页面,展示详细的错误信息 | -| html & text | 其他 | 是 | 重定向到 errorPageUrl | -| html & text | 其他 | 否 | onerror 自带的没有错误信息的简单错误页(不推荐) | -| json | local & unittest | - | json 对象,带详细的错误信息 | -| json | 其他 | - | json 对象,不带详细的错误信息 | +| HTML & TEXT | local & unittest | - | onerror 自带的错误页面,展示详细的错误信息 | +| HTML & TEXT | 其他 | 是 | 重定向到 errorPageUrl | +| HTML & TEXT | 其他 | 否 | onerror 自带的没有错误信息的简单错误页(不推荐) | +| JSON | local & unittest | - | JSON 对象,带详细的错误信息 | +| JSON | 其他 | - | json 对象,不带详细的错误信息 | ### errorPageUrl -onerror 插件的配置中支持 errorPageUrl 属性,当配置了 errorPageUrl 时,一旦用户请求线上应用的 html 页面异常,就会重定向到这个地址。 +onerror 插件的配置中支持 errorPageUrl 属性,当配置了 errorPageUrl 时,一旦用户请求线上应用的 HTML 页面异常,就会重定向到这个地址。 在 `config/config.default.js` 中 @@ -111,3 +111,56 @@ module.exports = { }, }; ``` + +## 404 + +框架并不会将服务端返回的 404 状态当做异常来处理,但是框架提供了当响应为 404 且没有返回 body 时的默认响应。 + +- 当请求被框架判定为需要 JSON 格式的响应时,会返回一段 JSON: + + ```json + { "message": "Not Found" } + ``` + +- 当请求被框架判定为需要 HTML 格式的响应时,会返回一段 HTML: + + ```html +

404 Not Found

+ ``` + +框架支持通过配置,将默认的 HTML 请求的 404 响应重定向到指定的页面。 + +```js +// config/config.default.js +module.exports = { + notfound: { + pageUrl: '/404.html', + }, +}; +``` + +### 自定义 404 响应 + +在一些场景下,我们需要自定义服务器 404 时的响应,和自定义异常处理一样,我们也只需要加入一个中间件即可对 404 做统一处理: + +```js +// app/middleware/notfound_handler.js +module.exports = () => { + return function* (next) { + yield next; + if (this.status === 404 && !this.body) { + if (this.acceptJSON) this.body = { error: 'Not Found' }; + else this.body = '

Page Not Found

'; + } + }; +}; +``` + +在配置中引入中间件: + +```js +// config/config.default.js +module.exports = { + middleware: [ 'notfoundHander' ], +}; +``` diff --git a/test/app/middleware/notfound.test.js b/test/app/middleware/notfound.test.js index 5d5f4c8ae7..e5182d29e8 100644 --- a/test/app/middleware/notfound.test.js +++ b/test/app/middleware/notfound.test.js @@ -1,6 +1,5 @@ 'use strict'; -const pedding = require('pedding'); const request = require('supertest'); const mm = require('egg-mock'); const utils = require('../../utils'); @@ -15,8 +14,7 @@ describe('test/app/middleware/notfound.test.js', () => { afterEach(mm.restore); - it('should 302 redirect to 404.html on production env', () => { - mm(app.config.notfound, 'enableRedirect', true); + it('should 302 redirect to 404.html', () => { return request(app.callback()) .get('/test/404') .set('Accept', 'test/html') @@ -24,13 +22,6 @@ describe('test/app/middleware/notfound.test.js', () => { .expect(302); }); - it('should show 404 on dev env', () => { - return request(app.callback()) - .get('/test/404') - .expect('

404 Not Found

Because you are in a non-prod environment, you will be looking at this page, otherwise it will jump to https://eggjs.org/404') - .expect(404); - }); - it('should 404 json response', () => { return request(app.callback()) .get('/test/404.json?ctoken=404') @@ -59,7 +50,7 @@ describe('test/app/middleware/notfound.test.js', () => { .expect(404); }); - describe('app.404.url=/404', () => { + describe('config.notfound.pageUrl = "/404"', () => { let app; before(() => { app = utils.app('apps/notfound-custom-404'); @@ -69,20 +60,32 @@ describe('test/app/middleware/notfound.test.js', () => { afterEach(mm.restore); - it('should 302 redirect to custom /404 on production env', done => { - done = pedding(2, done); - mm(app.config.notfound, 'enableRedirect', true); - - request(app.callback()) + it('should 302 redirect to custom /404 when required html', function* () { + yield request(app.callback()) .get('/test/404') .set('Accept', 'test/html') .expect('Location', '/404') - .expect(302, done); + .expect(302); - request(app.callback()) + yield request(app.callback()) .get('/404') .expect('Hi, this is 404') - .expect(200, done); + .expect(200); + }); + + it('should not avoid circular redirects', function* () { + mm(app.config.notfound, 'pageUrl', '/notfound'); + + yield request(app.callback()) + .get('/test/404') + .set('Accept', 'test/html') + .expect('Location', '/notfound') + .expect(302); + + yield request(app.callback()) + .get('/notfound') + .expect('

404 Not Found

config.notfound.pageUrl(/notfound)
is unimplemented

') + .expect(404); }); }); });