From efdd7d3716d8131d89ca599b14ffb3b1562aefe8 Mon Sep 17 00:00:00 2001 From: dead_horse Date: Wed, 1 Oct 2014 20:12:20 +0800 Subject: [PATCH] less strict for status, close #350 add res.message[=] --- docs/api/context.md | 2 ++ docs/api/response.md | 11 +++++- lib/application.js | 10 +++--- lib/context.js | 7 ++-- lib/response.js | 37 +++++++++++++++---- test/application.js | 76 ++++++++++++++++++++++++++++++++++++---- test/context/toJSON.js | 4 +-- test/response/inspect.js | 2 +- test/response/message.js | 29 +++++++++++++++ test/response/status.js | 25 +++++++++++-- 10 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 test/response/message.js diff --git a/docs/api/context.md b/docs/api/context.md index 0c708dd..a270f5e 100644 --- a/docs/api/context.md +++ b/docs/api/context.md @@ -171,6 +171,8 @@ koa uses [http-assert](https://github.com/jshttp/http-assert) for assertions. - `ctx.body=` - `ctx.status` - `ctx.status=` + - `ctx.message` + - `ctx.message=` - `ctx.length=` - `ctx.length` - `ctx.type=` diff --git a/docs/api/response.md b/docs/api/response.md index 00ba8a4..a450846 100644 --- a/docs/api/response.md +++ b/docs/api/response.md @@ -83,6 +83,15 @@ __NOTE__: don't worry too much about memorizing these strings, if you have a typo an error will be thrown, displaying this list so you can make a correction. +### response.message + + Get response status message. By default, `response.message` is + associated with `response.status`. + +### response.message= + + Set response status message to the given value. + ### response.length= Set response Content-Length to the given value. @@ -106,7 +115,7 @@ so you can make a correction. - `Object` json-stringified - `null` no content response - If `response.status` has not been set, Koa will automatically set the status to `200` or `204`. +If `response.status` has not been set, Koa will automatically set the status to `200` or `204`. #### String diff --git a/lib/application.js b/lib/application.js index 9be3123..1b1766d 100644 --- a/lib/application.js +++ b/lib/application.js @@ -4,15 +4,15 @@ var debug = require('debug')('koa:application'); var Emitter = require('events').EventEmitter; +var onFinished = require('on-finished'); +var response = require('./response'); var compose = require('koa-compose'); var isJSON = require('koa-is-json'); -var response = require('./response'); var context = require('./context'); var request = require('./request'); -var onFinished = require('on-finished'); +var statuses = require('statuses'); var Cookies = require('cookies'); var accepts = require('accepts'); -var status = require('statuses'); var assert = require('assert'); var Stream = require('stream'); var http = require('http'); @@ -187,7 +187,7 @@ function *respond(next) { var code = this.status; // ignore body - if (status.empty[code]) { + if (statuses.empty[code]) { // strip headers this.body = null; return res.end(); @@ -201,7 +201,7 @@ function *respond(next) { // status body if (null == body) { this.type = 'text'; - body = status[code]; + body = this.message || String(code); if (body) this.length = Buffer.byteLength(body); return res.end(body); } diff --git a/lib/context.js b/lib/context.js index 48c5f71..6fe422c 100644 --- a/lib/context.js +++ b/lib/context.js @@ -6,8 +6,8 @@ var createError = require('http-errors'); var httpAssert = require('http-assert'); var delegate = require('delegates'); +var statuses = require('statuses'); var assert = require('assert'); -var http = require('http'); /** * Context prototype. @@ -126,10 +126,10 @@ var proto = module.exports = { if ('ENOENT' == err.code) err.status = 404; // default to 500 - if ('number' != typeof err.status || !http.STATUS_CODES[err.status]) err.status = 500; + if ('number' != typeof err.status || !statuses[err.status]) err.status = 500; // respond - var code = http.STATUS_CODES[err.status]; + var code = statuses[err.status]; var msg = err.expose ? err.message : code; this.status = err.status; this.length = Buffer.byteLength(msg); @@ -148,6 +148,7 @@ delegate(proto, 'response') .method('vary') .method('set') .access('status') + .access('message') .access('body') .access('length') .access('type') diff --git a/lib/response.js b/lib/response.js index 2420b30..7cef359 100644 --- a/lib/response.js +++ b/lib/response.js @@ -5,11 +5,11 @@ var ensureErrorHandler = require('error-inject'); var getType = require('mime-types').contentType; +var onFinish = require('on-finished'); var isJSON = require('koa-is-json'); var escape = require('escape-html'); -var onFinish = require('on-finished'); var typeis = require('type-is').is; -var status = require('statuses'); +var statuses = require('statuses'); var destroy = require('destroy'); var assert = require('assert'); var http = require('http'); @@ -69,10 +69,33 @@ module.exports = { set status(code) { assert('number' == typeof code, 'status code must be a number'); - assert(http.STATUS_CODES[code], 'invalid status code: ' + code); + assert(statuses[code], 'invalid status code: ' + code); this._explicitStatus = true; this.res.statusCode = code; - if (this.body && status.empty[code]) this.body = null; + this.res.statusMessage = statuses[code]; + if (this.body && statuses.empty[code]) this.body = null; + }, + + /** + * Get response status message + * + * @return {String} + * @api public + */ + + get message() { + return this.res.statusMessage || statuses[this.status]; + }, + + /** + * Set response status message + * + * @param {String} msg + * @api public + */ + + set message(msg) { + this.res.statusMessage = msg; }, /** @@ -99,7 +122,7 @@ module.exports = { // no content if (null == val) { - if (!status.empty[this.status]) this.status = 204; + if (!statuses.empty[this.status]) this.status = 204; this.res.removeHeader('Content-Type'); this.res.removeHeader('Content-Length'); this.res.removeHeader('Transfer-Encoding'); @@ -223,7 +246,7 @@ module.exports = { this.set('Location', url); // status - if (!status.redirect[this.status]) this.status = 302; + if (!statuses.redirect[this.status]) this.status = 302; // html if (this.ctx.accepts('html')) { @@ -451,7 +474,7 @@ module.exports = { toJSON: function(){ return { status: this.status, - string: http.STATUS_CODES[this.status], + message: this.message, header: this.header } } diff --git a/test/application.js b/test/application.js index f8ad932..41b7abc 100644 --- a/test/application.js +++ b/test/application.js @@ -1,5 +1,6 @@ var request = require('supertest'); +var statuses = require('statuses'); var assert = require('assert'); var http = require('http'); var koa = require('..'); @@ -487,6 +488,69 @@ describe('app.respond', function(){ }) }) }) + + describe('with custom status=700', function(){ + it('should respond with the associated status message', function (done){ + var app = koa(); + statuses['700'] = 'custom status'; + + app.use(function *(){ + this.status = 700; + }) + + var server = app.listen(); + + request(server) + .get('/') + .expect(700) + .expect('custom status') + .end(function(err, res){ + if (err) return done(err); + res.res.statusMessage.should.equal('custom status'); + done(); + }) + }) + }) + + describe('with custom statusMessage=ok', function(){ + it('should respond with the custom status message', function (done){ + var app = koa(); + + app.use(function *(){ + this.status = 200; + this.message = 'ok'; + }) + + var server = app.listen(); + + request(server) + .get('/') + .expect(200) + .expect('ok') + .end(function(err, res){ + if (err) return done(err); + res.res.statusMessage.should.equal('ok'); + done(); + }) + }) + }) + + describe('with custom status without message', function (){ + it('should respond with the status code number', function (done){ + var app = koa(); + + app.use(function *(){ + this.res.statusCode = 701; + }) + + var server = app.listen(); + + request(server) + .get('/') + .expect(701) + .expect('701', done); + }) + }) }) describe('when .body is a null', function(){ @@ -909,12 +973,12 @@ describe('app.respond', function(){ describe('app.context', function(){ var app1 = koa(); - app1.context.message = 'hello'; + app1.context.msg = 'hello'; var app2 = koa(); it('should merge properties', function(done){ app1.use(function *(next){ - assert.equal(this.message, 'hello') + assert.equal(this.msg, 'hello') this.status = 204 }); @@ -925,7 +989,7 @@ describe('app.context', function(){ it('should not affect the original prototype', function(done){ app2.use(function *(next){ - assert.equal(this.message, undefined) + assert.equal(this.msg, undefined) this.status = 204; }); @@ -965,12 +1029,12 @@ describe('app.request', function(){ describe('app.response', function(){ var app1 = koa(); - app1.response.message = 'hello'; + app1.response.msg = 'hello'; var app2 = koa(); it('should merge properties', function(done){ app1.use(function *(next){ - assert.equal(this.response.message, 'hello') + assert.equal(this.response.msg, 'hello') this.status = 204 }); @@ -981,7 +1045,7 @@ describe('app.response', function(){ it('should not affect the original prototype', function(done){ app2.use(function *(next){ - assert.equal(this.response.message, undefined) + assert.equal(this.response.msg, undefined) this.status = 204; }); diff --git a/test/context/toJSON.js b/test/context/toJSON.js index bc3b159..d6e11a0 100644 --- a/test/context/toJSON.js +++ b/test/context/toJSON.js @@ -25,11 +25,11 @@ describe('ctx.toJSON()', function(){ res.should.eql({ status: 200, - string: 'OK', + message: 'OK', header: { 'content-type': 'text/html; charset=utf-8', 'content-length': '10' } }); }) -}) \ No newline at end of file +}) diff --git a/test/response/inspect.js b/test/response/inspect.js index 5170718..71a3aa3 100644 --- a/test/response/inspect.js +++ b/test/response/inspect.js @@ -19,7 +19,7 @@ describe('res.inspect()', function(){ res.inspect().should.eql({ body: 'hello', status: 200, - string: 'OK', + message: 'OK', header: { 'content-length': '5', 'content-type': 'text/plain; charset=utf-8' diff --git a/test/response/message.js b/test/response/message.js new file mode 100644 index 0000000..28384eb --- /dev/null +++ b/test/response/message.js @@ -0,0 +1,29 @@ + +var Stream = require('stream'); +var response = require('../context').response; + +describe('res.message', function(){ + it('should return the response status message', function(){ + var res = response(); + res.status = 200; + res.message.should.equal('OK'); + }) + + describe('when res.message not present', function(){ + it('should look up in statuses', function(){ + var res = response(); + res.res.statusCode = 200; + res.message.should.equal('OK'); + }) + }) +}) + +describe('res.message=', function(){ + it('should set response status message', function(){ + var res = response(); + res.status = 200; + res.message = 'ok'; + res.res.statusMessage.should.equal('ok'); + res.inspect().message.should.equal('ok'); + }) +}) diff --git a/test/response/status.js b/test/response/status.js index b76da75..aa5972a 100644 --- a/test/response/status.js +++ b/test/response/status.js @@ -1,6 +1,7 @@ var response = require('../context').response; var request = require('supertest'); +var statuses = require('statuses'); var assert = require('assert'); var koa = require('../..'); @@ -14,7 +15,7 @@ describe('res.status=', function(){ }) it('should not throw', function(){ - assert.doesNotThrow(function() { + assert.doesNotThrow(function() { response().status = 403; }); }) @@ -22,16 +23,34 @@ describe('res.status=', function(){ describe('and invalid', function(){ it('should throw', function(){ - assert.throws(function() { + assert.throws(function() { response().status = 999; }, 'invalid status code: 999'); }) }) + + describe('and custom status', function (){ + before(function () { + statuses['700'] = 'custom status'; + }) + + it('should set the status', function (){ + var res = response(); + res.status = 700; + res.status.should.equal(700); + }) + + it('should not throw', function(){ + assert.doesNotThrow(function() { + response().status = 700; + }); + }) + }) }) describe('when a status string', function(){ it('should throw', function(){ - assert.throws(function() { + assert.throws(function() { response().status = 'forbidden'; }, 'status code must be a number'); })