Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: app.onError for handling error #111

Merged
merged 1 commit into from
Feb 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,6 @@ app.use('/message/*', async (c, next) => {
app.get('/message/hello', (c) => c.text('Hello Middleware!'))
```

### Handling Error

```js
app.use('*', async (c, next) => {
try {
await next()
} catch (err) {
console.error(`${err}`)
c.res = c.text('Custom Error Message', { status: 500 })
}
})
```

## Context

To handle Request and Reponse, you can use Context object:
Expand Down Expand Up @@ -332,7 +319,7 @@ app.get('*', async c => {
})
```

## `404 Not Found` Response
## Not Found

If you want, you can set the default `404 Not Found` Response:

Expand All @@ -342,6 +329,16 @@ app.notFound = (c) => {
}
```

## Error handling

You can handle errors in your way:

```js
app.onError = (err, c) => {
return c.text(`This is error message: ${err.mssage}`, 500)
}
```

## fire

`app.fire()` do:
Expand Down
10 changes: 8 additions & 2 deletions src/compose.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Context } from './context'

// Based on the code in the MIT licensed `koa-compose` package.
export const compose = <T>(middleware: Function[]) => {
export const compose = <T>(middleware: Function[], onError?: Function) => {
const errors: Error[] = []
return function (context: T, next?: Function) {
let index = -1
Expand All @@ -13,7 +15,11 @@ export const compose = <T>(middleware: Function[]) => {
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1))).catch((e) => {
errors.push(e)
throw errors[0] // XXX
if (onError && context instanceof Context) {
context.res = onError(errors[0], context)
} else {
throw errors[0]
}
})
} catch (err) {
return Promise.reject(err)
Expand Down
23 changes: 8 additions & 15 deletions src/hono.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class Hono {

middleware.push(wrappedHandler)

const composed = compose<Context>(middleware)
const composed = compose<Context>(middleware, this.onError)
const c = new Context(request, { env: env, event: event, res: null })
c.notFound = () => this.notFound(c)

Expand All @@ -191,15 +191,11 @@ export class Hono {
}

async handleEvent(event: FetchEvent): Promise<Response> {
return this.dispatch(event.request, {}, event).catch((err: Error) => {
return this.onError(err)
})
return this.dispatch(event.request, {}, event)
}

async fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response> {
return this.dispatch(request, env, event).catch((err: Error) => {
return this.onError(err)
})
return this.dispatch(request, env, event)
}

fire() {
Expand All @@ -208,17 +204,14 @@ export class Hono {
})
}

onError(err: Error) {
console.error(`${err}`)
// Default error Response
onError(err: Error, c: Context) {
console.error(`${err.message}`)
const message = 'Internal Server Error'
return new Response(message, {
status: 500,
headers: {
'Content-Length': message.length.toString(),
},
})
return c.text(message, 500)
}

// Default 404 not found Response
notFound(c: Context) {
const message = 'Not Found'
return c.text(message, 404)
Expand Down
7 changes: 1 addition & 6 deletions src/middleware/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,7 @@ export const logger = (fn = console.log) => {

const start = Date.now()

try {
await next()
} catch (e) {
log(fn, LogPrefix.Error, method, path, c.res.status || 500, time(start))
throw e
}
await next()

const len = parseFloat(c.res.headers.get('Content-Length'))
const contentLength = isNaN(len) ? '0' : len < 1024 ? `${len}b` : `${len / 1024}kB`
Expand Down
32 changes: 15 additions & 17 deletions test/hono.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,32 +313,30 @@ describe('Error handle', () => {
const app = new Hono()

app.get('/error', () => {
throw 'This is Error'
throw new Error('This is Error')
})

app.use('/error', async (c, next) => {
try {
await next()
} catch (err) {
c.res = new Response('Custom Error Message', { status: 500 })
c.res.headers.append('debug', String(err))
}
app.use('/error-middleware', async () => {
throw new Error('This is Middleware Error')
})

app.onError = (err, c) => {
c.header('debug', err.message)
return c.text('Custom Error Message', 500)
}

it('Custom Error Message', async () => {
const req = new Request('https://example.com/error')
const res = await app.dispatch(req)
let req = new Request('https://example.com/error')
let res = await app.dispatch(req)
expect(res.status).toBe(500)
expect(await res.text()).toBe('Custom Error Message')
expect(res.headers.get('debug')).toBe('This is Error')
})

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
app.get('/text', () => 'text')
it('If return not Reponse object', async () => {
const req = new Request('https://example.com/text')
expect(app.dispatch(req)).rejects.toThrowError(TypeError)
req = new Request('https://example.com/error-middleware')
res = await app.dispatch(req)
expect(res.status).toBe(500)
expect(await res.text()).toBe('Custom Error Message')
expect(res.headers.get('debug')).toBe('This is Middleware Error')
})
})

Expand Down