Как вернуть 202 Accepted, а затем продолжить обработку запроса в Nest.js

Мне нужно действие контроллера, которое:

  1. Авторизует звонок
  2. Проверяет это
  3. Возвращает статус "Принято 202".
  4. Продолжает обработку запроса
  5. Выполняет вызов внешнего API с результатами ранее принятого и теперь обработанного запроса.

Первые два пункта просты, я использую AuthGuard, затем class-validator. Но я не знаю, как вернуть ответ HTTP и продолжить обработку.

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

Также, может быть, есть другой способ получше? Никакого перехватчика, а просто поместить всю логику потока в контроллер? Другой вариант?


person Forseti    schedule 08.11.2018    source источник


Ответы (2)


Я ожидаю, что у вас есть служба, которая выполняет вызов внешнего API (асинхронно) и возвращает либо Promise, либо Observable:

@Injectable()
export class ExternalApiService {
  constructor(private readonly httpService: HttpService) {}

  makeApiCall(data): Observable<AxiosResponse<any>> {
    return this.httpService.post('https://external.api', data);
  }
}

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

Контроллер

@Post()
@UseGuards(AuthGuard('jwt'))
@HttpCode(HttpStatus.ACCEPTED)
async post(@Body(new ValidationPipe()) myDataDto: MyDataDto) {
  // The preprocessing might throw an exception, so we need to wait for the result
  const preprocessedData = await this.preprocessService.preprocess(myDataDto);
                           ^^^^^
  // We do not need the result to respond to the request, so no await
  this.externalApiService.makeApiCall(preprocessedData);
  return 'Your data is being processed.';
}

Когда вы делаете асинхронные вызовы, выполнение вашего метода контроллера будет ждать только в том случае, если вы явно укажете это, используя async/await или вернув Promise / an Observable.

В этом примере мы хотим дождаться результата this.preprocessService.preprocess(myDataDto), прежде чем отправлять ответ. Мы делаем это с помощью await (метод должен быть объявлен как async).

Мы хотим сделать запрос к внешнему API, но нам не нужен результат для нашего ответа. Поскольку мы здесь не используем await, он выполнит http-вызов и немедленно выполнит следующую строку (оператор возврата), не дожидаясь результата.

person Kim Kern    schedule 09.11.2018
comment
Если бы я хотел использовать это для вызова внутреннего API, я определенно мог бы просто сделать полный http-пост, но не уверен, как вызвать свой внутренний наблюдаемый объект напрямую, не дожидаясь его. - person KyleMit; 02.11.2020
comment
чтобы ответить на мой собственный комментарий, мое решение состоит в том, чтобы вместо того, чтобы возвращать наблюдаемое контроллеру гнезда и полагаться на гнездо для вызова subscribe за кулисами, я напрямую вызываю .subscribe() на наблюдаемое, а затем сразу же возвращаю значение - person KyleMit; 02.11.2020
comment
@KyleMit Извините за мой поздний ответ, Кайл, последние пару недель я был очень занят. - Совершенно верно, хороший вариант, рад, что вы так быстро нашли решение. В качестве альтернативы, если вы предпочитаете работать с обещаниями, вы также можете вызвать toPromise в Observable. - person Kim Kern; 14.11.2020
comment
Привет, Ким, большое спасибо за ответ и обновление. Первоначально я использовал toPromise, но затем получил несколько предупреждений о том, что toPromise устарел в v7 и удалено в v8 - person KyleMit; 16.11.2020

Если ваша служба возвращает обещание, вы можете сделать что-то вроде следующего, API вернет 202, пока обработка продолжается. Конечно, любая обработка с помощью 'then' происходит после завершения 'beginProcessing'.

@Post()
@HttpCode(HttpStatus.ACCEPTED)
beginProcessing(@Req() request, @Body() data: Data): void {
    this.service.process(data).then(...);
}
person Rich Duncan    schedule 09.11.2018