TypeScript и Koa 2: проблема async/await с глобальным обработчиком ошибок

Я пишу приложение с TypeScript, а также с Koa 2.

Однако проблема, с которой я сталкиваюсь, заключается в том, что мой глобальный обработчик ошибок Koa не улавливает ошибки, которые были выброшены в моем приложении.

Возьмем, к примеру, следующее промежуточное ПО (это самое первое промежуточное ПО перед загрузкой каких-либо маршрутов):

app.use(async(ctx, next) => {
    console.log("ErrorHandler loaded...");
    try {
        console.log("Trying for error...");
        await next();
    } catch (err) {
        console.log("Caught error...");
        ctx.status = err.status || 500;
        ctx.response.body = "Error: " + err.message;
    }
});

При доступе к моим маршрутам я вижу, что обработчик ошибок загружен и работает блок try.

Однако, если я выдаю ошибку в маршруте (независимо от того, использую ли я throw или ctx.throw), все, что я получаю, это сообщение об ошибке по умолчанию. справиться с этим.

Теперь рассмотрим следующий транспилированный JavaScript:

app.use((ctx, next) => __awaiter(this, void 0, void 0, function* () {
    console.log("ErrorHandler loaded...");
    try {
        console.log("Trying for error...");
        yield next();
    }
    catch (err) {
        console.log("Caught error...");
        ctx.status = err.status || 500;
        ctx.response.body = "Error: " + err.message;
    }
}));

Вопрос двоякий:

  1. Мое приложение не может поймать выброшенные ошибки из-за транспиляции? Я имею в виду, будет ли это работать, если транспилируемый JavaScript будет использовать ключевые слова async и await, а не транспилировать его в генераторы, использующие yield?
  2. Если вышесказанное верно: есть ли способ писать приложения Koa 2 с помощью TypeScript сейчас?

Редактировать

Я нашел решение, чтобы мои ошибки выбрасывались, но я до сих пор не понимаю, почему Koa их не ловит.

Этот цикл for of выполняет итерацию массива контроллеров, которые я загружаю динамически. Для каждого контроллера я привязываю соответствующий метод (action.method) класса (action.target) к маршруту (action.route), используя указанный глагол Http (action.type).

Однако я также привязываю контекст к методу, чтобы гарантировать, что в соответствии с соглашениями Koa this привязан к Context:

for (let action of actionMetadata) {
    router[action.type.toLowerCase()](action.route, (ctx, next) => {
        (new action.target)[action.method].bind(ctx)(ctx, next);
    });
}

Вышеприведенное вызывает проблему, когда ошибки не обнаруживаются.

Для сравнения, приведенный ниже код работает: ошибки теперь перехватываются Koa. Но это означает, что this теперь уже не Context.

Я могу смириться с этим, так как Context является первым параметром в моих маршрутах, но я не понимаю, почему ошибка не перехватывается, поскольку все промежуточное ПО, следующее за обработчиком ошибок (это мое самое первое промежуточное ПО), должно работать в его блоке try:

for (let action of actionMetadata) {
    router[action.type.toLowerCase()](action.route, (new action.target )[action.method]);
}

person JDR    schedule 21.08.2016    source источник
comment
Предполагается использовать транспилированный код. Нет «ключевых слов async и await», потому что это предложение не стандартизировано и еще не реализовано. Я предполагаю, что виноваты другие части приложения и бета-статус Koa 2.   -  person Estus Flask    schedule 22.08.2016
comment
Это работает хорошо для меня. Можете ли вы показать больше своего кода, например, как вы выдаете ошибку? Порядок промежуточного программного обеспечения и то, что когда-либо может быть полезным.   -  person Nitzan Tomer    schedule 22.08.2016
comment
Привет! Спасибо за участие. У меня получилось, но я все еще думаю, что моя предыдущая реализация тоже должна была сработать. Поэтому я добавил обновление к своему вопросу.   -  person JDR    schedule 22.08.2016
comment
Почему вы вообще используете классы? Почему вы создаете там экземпляр класса вместо того, чтобы создавать их с самого начала, а затем просто обращаться к экземплярам? И затем, если вы не хотите, чтобы методы экземпляра имели экземпляр как this, тогда какой смысл иметь эти функции в классе?   -  person Nitzan Tomer    schedule 22.08.2016
comment
К первому пункту: потому что мне нравится структура, которую я получаю, используя декораторы методов, такие как @Route("/view/:id", HttpVerb.Get). К последнему пункту: нет никакого смысла - это был чистый пережиток моей попытки следовать соглашениям Коа. this. Как я уже сказал: ctx — это первый параметр, и мне не нужно, чтобы this был контекстом. Помимо выбора дизайна, я думаю, мне просто трудно понять, почему первая настройка не перехватывает выброшенные исключения, поскольку нет дополнительных try, которые потенциально могли бы их поглотить.   -  person JDR    schedule 22.08.2016


Ответы (1)


Верно, похоже, это была чистая ошибка пользователя в том, что он не гарантирует, что каждое промежуточное ПО возвращает обещание (или является асинхронной функцией).

async в следующем — это все, чего не хватало:

for (let action of actionMetadata) {
    router[action.type.toLowerCase()](action.route, async (ctx, next) => {
        (new action.target)[action.method].bind(ctx)(ctx, next);
    });
}
person JDR    schedule 18.09.2016