title | layout | thread | date | author | categories | tags | excerpt | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
\[Перевод\] Освоение ПО промежуточного слоя Koa | post | 181 | 2017-11-10 | Joe Jiang | documents |
|
Koa2 был недавно выпущен, так как Node незаметно реализовал использование асинхронного ожидания. Express по-прежнему имеет преимущество в этом конкурсе популярности, но я с удовольствием использую Koa2 с момента его выпуска и всегда боялся возвращаться к использованию Express для старых проектов... |
С узлом молчареализует асинхронное ожиданиеиспользование, Koa2 также недавновыпускать. Express по-прежнему имеет преимущество в этом конкурсе популярности, но я с удовольствием использую его с тех пор, как был выпущен Koa2, и всегда боюсь возвращаться к старым проектам. Выражать.
я блуждаю иногдаKoa GitterДля всех ответов на вопросы, и я ответил, что большинство вопросов связаны с промежуточным программным обеспечением и волшебным по всем вопросам, поэтому я на самом деле настроил хорошую запись в этом вопросе.
Есть много новичков в Koa, которые использовали Express раньше, поэтому я буду проводить много сравнений между ними.
Этот пост предназначен для тех, кто плохо знаком с Koa, и для тех, кто рассматривает возможность использования Koa в своем следующем проекте.
основная концепция
Начнем с самого важного. В Koa и Express все, что касается HTTP-запросов, делается внутри промежуточного программного обеспечения, и самое главное — это понимать.проход продолжения промежуточного программного обеспеченияКонцепция чего-либо. Звучит красиво, но это не так. Идея состоит в том, что как только промежуточное ПО выполнило свою задачу, оно может вызвать следующее промежуточное ПО в цепочке.
Express
const express = require('express')
const app = express()
// Middleware 1
app.use((req, res, next) => {
res.status(200)
console.log('Setting status')
// Call the next middleware
next()
})
// Middleware 2
app.use((req, res) => {
console.log('Setting body')
res.send(`Hello from Express`)
})
app.listen(3001, () => console.log('Express app listening on 3001'))
Koa
const Koa = require('koa')
const app = new Koa()
// Middleware 1
app.use(async (ctx, next) => {
ctx.status = 200
console.log('Setting status')
// Call the next middleware, wait for it to complete
await next()
})
// Middleware 2
app.use((ctx) => {
console.log('Setting body')
ctx.body = 'Hello from Koa'
})
app.listen(3002, () => console.log('Koa app listening on 3002'))
давайте использоватьcurl
Команда для проверки обоих:
$ curl http://localhost:3001
Hello from Express
$ curl http://localhost:3002
Hello from Koa
скопировать код
Оба примера делают одно и то же и оба выводят на терминал один и тот же вывод:
Setting status
Setting body
скопировать код
Это показывает, что в обоих случаях промежуточное ПО работает сверху вниз.
Большая разница здесь в том, что цепочка промежуточного программного обеспечения ExpressОбратный вызов на основе, а КоаОбещание на основеиз.
Посмотрим, опустим ли мы в обоих примерахnext()
,Что случится.
Express
$ curl http://localhost:3001
скопировать код
... это никогда не заканчивается. Это потому, что в Express вам нужно позвонитьnext()
Выберите между отправкой ответа и отправкой ответа, иначе запрос никогда не будет выполнен.
Koa
$ curl http://localhost:3002
OK
скопировать код
Ах, все Коа выполнит запрос, у него есть информация о коде состояния, но нет информации о теле. Так что второе промежуточное ПО не вызывается.
Но есть еще одна вещь, которая важна для Коа. если ты позвонишьnext()
, вы должны ждать его!
Вот лучшие примеры:
// Simple Promise delay
function delay (ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
app.use(async (ctx, next) => {
ctx.status = 200
console.log('Setting status')
next() // forgot await!
})
app.use(async (ctx) => {
await delay(1000) // simulate actual async behavior
console.log('Setting body')
ctx.body = 'Hello from Koa'
})
Давайте посмотрим, что произошло.
$ curl http://localhost:3002
OK
скопировать код
Ну, мы звонимnext()
, а без каких-либо сведений о теле прошло? Это связано с тем, что Koa завершает запрос после того, как цепочка обещаний промежуточного программного обеспечения была разрешена. Это означает, что когда мы устанавливаемctx.body
До,ответ отправлен клиенту!
Еще одна вещь, которую нужно понимать, это то, что если вы используете чистыйPromise.then()
заменятьasync-await
, ваше промежуточное ПО должно возвращать обещание. Когда возвращенное обещание будет разрешено, Koa восстановит предыдущее промежуточное ПО в это время.
app.use((ctx, next) => {
ctx.status = 200
console.log('Setting status')
// need to return here, not using async-await
return next()
})
Лучший пример с использованием чистых промисов:
// We don't call `next()` because
// we don't want anything else to happen.
app.use((ctx) => {
return delay(1000).then(() => {
console.log('Setting body')
ctx.body = 'Hello from Koa'
})
})
Koa Middleware — возможности, меняющие правила игры
В предыдущей главе я писал:
На этом этапе Koa восстановит предыдущее промежуточное ПО.
Это может вас немного разочаровать, позвольте мне объяснить.
В Express промежуточное ПО может быть вызвано толькоnext()
Выполняйте соответствующие операции до, а не после. как только ты позвонишьnext()
, запрос больше никогда не коснется этого промежуточного ПО. Это может быть немного разочаровывающим, люди (включая самих авторов Express) нашли несколько умных обходных путей, таких как просмотр потока ответов при написании заголовков, но это просто неудобно для обычного пользователя.
Например, для реализации промежуточного программного обеспечения, которое записывает время, необходимое для выполнения запроса, и отправляет его в заголовок X-ResponseTime, требуется кодовая точка «до следующего вызова» и кодовая точка «после следующего вызова». В Express это реализовано с помощью техники потокового наблюдения.
Давайте реализуем это в Koa.
async function responseTime (ctx, next) {
console.log('Started tracking response time')
const started = Date.now()
await next()
// once all middleware below completes, this continues
const ellapsed = (Date.now() - started) + 'ms'
console.log('Response time is:', ellapsed)
ctx.set('X-ResponseTime', ellapsed)
}
app.use(responseTime)
app.use(async (ctx, next) => {
ctx.status = 200
console.log('Setting status')
await next()
})
app.use(async (ctx) => {
await delay(1000)
console.log('Setting body')
ctx.body = 'Hello from Koa'
})
8 строк, это все, что нужно. Никакого сложного обнюхивания потока, просто потрясающе выглядящий асинхронно-ожидающий код. Давайте запустить его! здесь-i
Флаг указывает curl также показать нам заголовки ответа.
$ curl -i http://localhost:3002
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 14
X-ResponseTime: 1001ms
Date: Thu, 30 Mar 2017 12:52:48 GMT
Connection: keep-alive
Hello from Koa
скопировать код
Превосходно!我们在 HTTP 头部中找到了响应时间。让我们再看看终端上的日志吧,看看日志是以什么顺序输出的。
Started tracking response time
Setting status
Setting body
Response time is: 1001ms
скопировать код
Смотри, это все. Koa дает нам полный контроль над потоком ПО промежуточного слоя. Реализовать такие вещи, как аутентификация и обработка ошибок, будет очень просто!
обработка ошибок
Это моя любимая вещь в Koa, которая поддерживается мощной цепочкой промежуточного программного обеспечения Promise, подробно описанной выше.
На всякий случай давайте посмотрим, как мы можем сделать это в Express.
Express
Обработка ошибок выполняется в специально подписанном промежуточном программном обеспечении, которое необходимо добавить в конец цепочки, чтобы оно работало.
app.use((req, res) => {
if (req.query.greet !== 'world') {
throw new Error('can only greet "world"')
}
res.status(200)
res.send(`Hello ${req.query.greet} from Express`)
})
// Error handler
app.use((err, req, res, next) => {
if (!err) {
next()
return
}
console.log('Error handler:', err.message)
res.status(400)
res.send('Uh-oh: ' + err.message)
})
Это яркий пример. Если вы имеете дело с асинхронными ошибками от обратных вызовов или промисов, это может стать очень подробным. Например:
app.use((req, res, next) => {
loadCurrentWeather(req.query.city, (err, weather) => {
if (err) {
return next(err)
}
loadForecast(req.query.city, (err, forecast) => {
if (err) {
return next(err)
}
res.status(200).send({
weather: weather,
forecast: forecast
})
})
})
next()
})
Я полностью осознаю, что легче справиться с адом обратных вызовов с модулями, это просто для того, чтобы продемонстрировать, что простая обработка ошибок становится громоздкой в Express, не говоря уже о том, что вам также нужно подумать об асинхронных ошибках против синхронных ошибок и т. д.
Koa
Обработка ошибок также осуществляется с помощью промисов. Коа всегда рядом с намиnext()
Завернуто в обещание, поэтому нам даже не нужно беспокоиться об ошибках асинхронности и синхронизации.
ПО промежуточного слоя для обработки ошибок выполняется в самом верху, потому что "обходить" для каждого последующего ПО промежуточного слоя. Это означает, что любые ошибки, добавленные после обработки ошибок, будут обнаружены (Да, почувствуй!)
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = 400
ctx.body = `Uh-oh: ${err.message}`
console.log('Error handler:', err.message)
}
})
app.use(async (ctx) => {
if (ctx.query.greet !== 'world') {
throw new Error('can only greet "world"')
}
console.log('Sending response')
ctx.status = 200
ctx.body = `Hello ${ctx.query.greet} from Koa`
})
да, одинtry-catch
. Для обработки ошибок *как уместно! *Нетasync-await
Способ следующий:
app.use((ctx, next) => {
return next().catch(err => {
ctx.status = 400
ctx.body = `Uh-oh: ${err.message}`
console.log('Error handler:', err.message)
})
})
Начнем с ошибки:
$ curl http://localhost:3002?greet=jeff
Uh-oh: can only greet "world"
скопировать код
Консоль выводит, как и ожидалось:
Error handler: can only greet "world"
скопировать код
маршрутизация
В отличие от Express, в Koa почти ничего нет в наличии. Там нет бодипарсера и маршрутизации.
В Коа есть много вариантов маршрутизации, таких какkoa-route
а такжеkoa-router
. Я предпочитаю последнее.
Express
Маршрутизация в Express встроена.
app.get('/todos', (req, res) => {
res.status(200).send([{
id: 1,
text: 'Switch to Koa'
}, {
id: 2,
text: '???'
}, {
id: 3,
text: 'Profit'
}])
})
Koa
В этом примере я выбираюkoa-router
Потому что это маршрут, который я использую.
const Router = require('koa-router')
const router = new Router()
router.get('/todos', (ctx) => {
ctx.status = 200
ctx.body = [{
id: 1,
text: 'Switch to Koa',
completed: true
}, {
id: 2,
text: '???',
completed: true
}, {
id: 3,
text: 'Profit',
completed: true
}]
})
app.use(router.routes())
// makes sure a 405 Method Not Allowed is sent
app.use(router.allowedMethods())
В заключение
Коа крут. Полный контроль над цепочкой промежуточного программного обеспечения и тот факт, что она основана на промисах, позволяет легко манипулировать всем. уже не вездеif (err) return next(err)
И только обещания.
С помощью сверхмощных обработчиков ошибок мы можем более изящно удалять ошибки из пути нашего кода (например, ошибки проверки, нарушения бизнес-логики).
Вот список промежуточного программного обеспечения, которое я часто использую (в произвольном порядке):
- koa-compress
- koa-respond
- kcors
- koa-convert
- koa-bodyparser
- koa-compose
- koa-router
Знает лучше: не все промежуточные программы доступны на основе Koa 2, поэтому к ним можно получить доступ во время выполнения черезkoa-convert
конвертировать, так что не волнуйтесь.
Оригинальная ссылка
Mastering Koa Middleware, по твиттеру@Jeffijoe