less strict for status, close #350

add res.message[=]
master
dead_horse 2014-10-01 20:12:20 +08:00
parent 8774979e13
commit efdd7d3716
10 changed files with 175 additions and 28 deletions

View File

@ -171,6 +171,8 @@ koa uses [http-assert](https://github.com/jshttp/http-assert) for assertions.
- `ctx.body=` - `ctx.body=`
- `ctx.status` - `ctx.status`
- `ctx.status=` - `ctx.status=`
- `ctx.message`
- `ctx.message=`
- `ctx.length=` - `ctx.length=`
- `ctx.length` - `ctx.length`
- `ctx.type=` - `ctx.type=`

View File

@ -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 if you have a typo an error will be thrown, displaying this list
so you can make a correction. 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= ### response.length=
Set response Content-Length to the given value. Set response Content-Length to the given value.
@ -106,7 +115,7 @@ so you can make a correction.
- `Object` json-stringified - `Object` json-stringified
- `null` no content response - `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 #### String

View File

@ -4,15 +4,15 @@
var debug = require('debug')('koa:application'); var debug = require('debug')('koa:application');
var Emitter = require('events').EventEmitter; var Emitter = require('events').EventEmitter;
var onFinished = require('on-finished');
var response = require('./response');
var compose = require('koa-compose'); var compose = require('koa-compose');
var isJSON = require('koa-is-json'); var isJSON = require('koa-is-json');
var response = require('./response');
var context = require('./context'); var context = require('./context');
var request = require('./request'); var request = require('./request');
var onFinished = require('on-finished'); var statuses = require('statuses');
var Cookies = require('cookies'); var Cookies = require('cookies');
var accepts = require('accepts'); var accepts = require('accepts');
var status = require('statuses');
var assert = require('assert'); var assert = require('assert');
var Stream = require('stream'); var Stream = require('stream');
var http = require('http'); var http = require('http');
@ -187,7 +187,7 @@ function *respond(next) {
var code = this.status; var code = this.status;
// ignore body // ignore body
if (status.empty[code]) { if (statuses.empty[code]) {
// strip headers // strip headers
this.body = null; this.body = null;
return res.end(); return res.end();
@ -201,7 +201,7 @@ function *respond(next) {
// status body // status body
if (null == body) { if (null == body) {
this.type = 'text'; this.type = 'text';
body = status[code]; body = this.message || String(code);
if (body) this.length = Buffer.byteLength(body); if (body) this.length = Buffer.byteLength(body);
return res.end(body); return res.end(body);
} }

View File

@ -6,8 +6,8 @@
var createError = require('http-errors'); var createError = require('http-errors');
var httpAssert = require('http-assert'); var httpAssert = require('http-assert');
var delegate = require('delegates'); var delegate = require('delegates');
var statuses = require('statuses');
var assert = require('assert'); var assert = require('assert');
var http = require('http');
/** /**
* Context prototype. * Context prototype.
@ -126,10 +126,10 @@ var proto = module.exports = {
if ('ENOENT' == err.code) err.status = 404; if ('ENOENT' == err.code) err.status = 404;
// default to 500 // 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 // respond
var code = http.STATUS_CODES[err.status]; var code = statuses[err.status];
var msg = err.expose ? err.message : code; var msg = err.expose ? err.message : code;
this.status = err.status; this.status = err.status;
this.length = Buffer.byteLength(msg); this.length = Buffer.byteLength(msg);
@ -148,6 +148,7 @@ delegate(proto, 'response')
.method('vary') .method('vary')
.method('set') .method('set')
.access('status') .access('status')
.access('message')
.access('body') .access('body')
.access('length') .access('length')
.access('type') .access('type')

View File

@ -5,11 +5,11 @@
var ensureErrorHandler = require('error-inject'); var ensureErrorHandler = require('error-inject');
var getType = require('mime-types').contentType; var getType = require('mime-types').contentType;
var onFinish = require('on-finished');
var isJSON = require('koa-is-json'); var isJSON = require('koa-is-json');
var escape = require('escape-html'); var escape = require('escape-html');
var onFinish = require('on-finished');
var typeis = require('type-is').is; var typeis = require('type-is').is;
var status = require('statuses'); var statuses = require('statuses');
var destroy = require('destroy'); var destroy = require('destroy');
var assert = require('assert'); var assert = require('assert');
var http = require('http'); var http = require('http');
@ -69,10 +69,33 @@ module.exports = {
set status(code) { set status(code) {
assert('number' == typeof code, 'status code must be a number'); 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._explicitStatus = true;
this.res.statusCode = code; 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 // no content
if (null == val) { 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-Type');
this.res.removeHeader('Content-Length'); this.res.removeHeader('Content-Length');
this.res.removeHeader('Transfer-Encoding'); this.res.removeHeader('Transfer-Encoding');
@ -223,7 +246,7 @@ module.exports = {
this.set('Location', url); this.set('Location', url);
// status // status
if (!status.redirect[this.status]) this.status = 302; if (!statuses.redirect[this.status]) this.status = 302;
// html // html
if (this.ctx.accepts('html')) { if (this.ctx.accepts('html')) {
@ -451,7 +474,7 @@ module.exports = {
toJSON: function(){ toJSON: function(){
return { return {
status: this.status, status: this.status,
string: http.STATUS_CODES[this.status], message: this.message,
header: this.header header: this.header
} }
} }

View File

@ -1,5 +1,6 @@
var request = require('supertest'); var request = require('supertest');
var statuses = require('statuses');
var assert = require('assert'); var assert = require('assert');
var http = require('http'); var http = require('http');
var koa = require('..'); 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(){ describe('when .body is a null', function(){
@ -909,12 +973,12 @@ describe('app.respond', function(){
describe('app.context', function(){ describe('app.context', function(){
var app1 = koa(); var app1 = koa();
app1.context.message = 'hello'; app1.context.msg = 'hello';
var app2 = koa(); var app2 = koa();
it('should merge properties', function(done){ it('should merge properties', function(done){
app1.use(function *(next){ app1.use(function *(next){
assert.equal(this.message, 'hello') assert.equal(this.msg, 'hello')
this.status = 204 this.status = 204
}); });
@ -925,7 +989,7 @@ describe('app.context', function(){
it('should not affect the original prototype', function(done){ it('should not affect the original prototype', function(done){
app2.use(function *(next){ app2.use(function *(next){
assert.equal(this.message, undefined) assert.equal(this.msg, undefined)
this.status = 204; this.status = 204;
}); });
@ -965,12 +1029,12 @@ describe('app.request', function(){
describe('app.response', function(){ describe('app.response', function(){
var app1 = koa(); var app1 = koa();
app1.response.message = 'hello'; app1.response.msg = 'hello';
var app2 = koa(); var app2 = koa();
it('should merge properties', function(done){ it('should merge properties', function(done){
app1.use(function *(next){ app1.use(function *(next){
assert.equal(this.response.message, 'hello') assert.equal(this.response.msg, 'hello')
this.status = 204 this.status = 204
}); });
@ -981,7 +1045,7 @@ describe('app.response', function(){
it('should not affect the original prototype', function(done){ it('should not affect the original prototype', function(done){
app2.use(function *(next){ app2.use(function *(next){
assert.equal(this.response.message, undefined) assert.equal(this.response.msg, undefined)
this.status = 204; this.status = 204;
}); });

View File

@ -25,11 +25,11 @@ describe('ctx.toJSON()', function(){
res.should.eql({ res.should.eql({
status: 200, status: 200,
string: 'OK', message: 'OK',
header: { header: {
'content-type': 'text/html; charset=utf-8', 'content-type': 'text/html; charset=utf-8',
'content-length': '10' 'content-length': '10'
} }
}); });
}) })
}) })

View File

@ -19,7 +19,7 @@ describe('res.inspect()', function(){
res.inspect().should.eql({ res.inspect().should.eql({
body: 'hello', body: 'hello',
status: 200, status: 200,
string: 'OK', message: 'OK',
header: { header: {
'content-length': '5', 'content-length': '5',
'content-type': 'text/plain; charset=utf-8' 'content-type': 'text/plain; charset=utf-8'

29
test/response/message.js Normal file
View File

@ -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');
})
})

View File

@ -1,6 +1,7 @@
var response = require('../context').response; var response = require('../context').response;
var request = require('supertest'); var request = require('supertest');
var statuses = require('statuses');
var assert = require('assert'); var assert = require('assert');
var koa = require('../..'); var koa = require('../..');
@ -14,7 +15,7 @@ describe('res.status=', function(){
}) })
it('should not throw', function(){ it('should not throw', function(){
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
response().status = 403; response().status = 403;
}); });
}) })
@ -22,16 +23,34 @@ describe('res.status=', function(){
describe('and invalid', function(){ describe('and invalid', function(){
it('should throw', function(){ it('should throw', function(){
assert.throws(function() { assert.throws(function() {
response().status = 999; response().status = 999;
}, 'invalid status code: 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(){ describe('when a status string', function(){
it('should throw', function(){ it('should throw', function(){
assert.throws(function() { assert.throws(function() {
response().status = 'forbidden'; response().status = 'forbidden';
}, 'status code must be a number'); }, 'status code must be a number');
}) })