Add 404 support and some more error handling

This commit is contained in:
Jonatan Nilsson 2022-01-05 14:06:06 +00:00
parent 1290069287
commit f1cff5d9ea
4 changed files with 162 additions and 16 deletions

View file

@ -341,11 +341,6 @@ export class Flaska {
this._onreserror = handler this._onreserror = handler
} }
onerror(handler) {
assertIsHandler(handler, 'onerror()')
this._onerror = handler
}
before(handler) { before(handler) {
assertIsHandler(handler, 'before()') assertIsHandler(handler, 'before()')
this._before.push(handler) this._before.push(handler)
@ -427,6 +422,18 @@ export class Flaska {
requestStartInternal(ctx) { requestStartInternal(ctx) {
let route = this.routers[ctx.method].match(ctx.url) let route = this.routers[ctx.method].match(ctx.url)
if (!route) {
let middle = this._on404(ctx)
if (middle && middle.then) {
return middle.then(() => {
this.requestEnd(null, ctx)
}, err => {
this.requestEnd(err, ctx)
})
}
return this.requestEnd(null, ctx)
}
ctx.params = route.params ctx.params = route.params
if (route.middlewares.length) { if (route.middlewares.length) {
@ -467,7 +474,11 @@ export class Flaska {
requestEnd(err, ctx) { requestEnd(err, ctx) {
if (err) { if (err) {
try {
this._onerror(err, ctx) this._onerror(err, ctx)
} catch (err) {
this._backuperror(err, ctx)
}
} }
if (ctx.res.writableEnded) { if (ctx.res.writableEnded) {
return return

View file

@ -4,18 +4,11 @@ const port = 51024
const flaska = new Flaska({}, ) const flaska = new Flaska({}, )
flaska.get('/', function(ctx) { flaska.get('/', function(ctx) {
return new Promise(function(res, rej) {
ctx.body = { status: true } ctx.body = { status: true }
setTimeout(res, 1000)
})
}) })
flaska.after(function(ctx) { flaska.get('/error', function(ctx) {
if (ctx.aborted) { process.exit(1)
process.stdout.write("A")
} else {
process.stdout.write(".")
}
}) })
flaska.listen(port, function() { flaska.listen(port, function() {

View file

@ -274,6 +274,126 @@ t.describe('#requestStart()', function() {
}), createRes()) }), createRes())
}) })
t.test('calls 404 if route handler is not found', function(cb) {
const on404Error = spy()
let handler = function() {
throw new Error('should not be called')
}
let flaska = new Flaska({}, faker)
flaska.on404(on404Error)
flaska.get('/test', function() { throw new Error('should not be called') })
flaska.compile()
flaska.requestEnd = function(err, ctx) {
if (err) return cb(err)
try {
assert.ok(ctx)
assert.strictEqual(on404Error.callCount, 1)
assert.strictEqual(on404Error.firstCall[0], ctx)
cb()
} catch (err) { cb(err) }
}
flaska.requestStart(createReq({
url: '/nope',
method: 'GET',
}), createRes())
})
t.test('calls 404 if route handler is not found and supports promise', function(cb) {
let checkCtx = null
const assertError = new Error('should be seen')
const on404Error = function(ctx) {
checkCtx = ctx
return Promise.resolve().then(function() { return Promise.reject(assertError) })
}
let flaska = new Flaska({}, faker)
flaska.on404(on404Error)
flaska.get('/test', function() { throw new Error('should not be called') })
flaska.compile()
flaska.requestEnd = function(err, ctx) {
if (err && err !== assertError) return cb(err)
try {
assert.ok(err)
assert.ok(ctx)
assert.strictEqual(ctx, checkCtx)
assert.strictEqual(err, assertError)
cb()
} catch (err) { cb(err) }
}
flaska.requestStart(createReq({
url: '/nope',
method: 'GET',
}), createRes())
})
t.test(`should handle unexpected errors in on404 correctly`, function(cb) {
const assertError = new Error('should be seen')
let checkCtx = null
let flaska = new Flaska({}, faker)
flaska.on404(function(ctx) {
checkCtx = ctx
throw assertError
})
flaska.get('/test', function() { throw new Error('should not be called') })
flaska.compile()
flaska.requestEnd = function(err, ctx) {
if (err && err !== assertError) return cb(err)
try {
assert.ok(err)
assert.ok(ctx)
assert.strictEqual(ctx, checkCtx)
assert.strictEqual(err, assertError)
cb()
} catch (err) { cb(err) }
}
flaska.requestStart(createReq({
url: '/nope',
method: 'GET',
}), createRes())
})
t.test(`should handle unexpected errors in middleware correctly`, function(cb) {
const assertError = new Error('should be seen')
let checkCtx = null
let flaska = new Flaska({}, faker)
let middles = [function(ctx) {
checkCtx = ctx
throw assertError
}]
flaska.get('/test', middles, function() { throw new Error('should not be called') })
flaska.compile()
flaska.requestEnd = function(err, ctx) {
if (err && err !== assertError) return cb(err)
try {
assert.ok(err)
assert.ok(ctx)
assert.strictEqual(ctx, checkCtx)
assert.strictEqual(err, assertError)
cb()
} catch (err) { cb(err) }
}
flaska.requestStart(createReq({
url: '/test',
method: 'GET',
}), createRes())
})
t.test('should work with synchronous handler', function(cb) { t.test('should work with synchronous handler', function(cb) {
const assertBody = { a: 1 } const assertBody = { a: 1 }
let handler = function(ctx) { let handler = function(ctx) {

View file

@ -40,6 +40,28 @@ t.describe('#requestEnd()', function() {
flaska.requestEnd(assertError, ctx) flaska.requestEnd(assertError, ctx)
}) })
t.test('calls backup onerror correctly if error throws', function(cb) {
const assertErrorNotSeen = new Error('should not be seen')
const assertError = new Error('test')
let onFinish = function(body) {
try {
assert.strictEqual(ctx.status, 500)
assert.strictEqual(ctx.body.status, 500)
assert.strictEqual(ctx.body.message, 'Internal Server Error')
cb()
} catch (err) { cb(err) }
}
const ctx = createCtx({}, onFinish)
let flaska = new Flaska({}, fakerHttp, fakeStream)
flaska.onerror(function() {
throw assertError
})
flaska.requestEnd(assertErrorNotSeen, ctx)
})
t.test('call res and end correctly when dealing with objects', function(cb) { t.test('call res and end correctly when dealing with objects', function(cb) {
const assertStatus = 202 const assertStatus = 202
// Calculated manually just in case // Calculated manually just in case