NestJs-使用自定义过滤器和@Catch(MongoError)捕获MongoDB错误

时间:2019-02-04 19:36:22

标签: javascript node.js mongodb typescript nestjs

我正在使用NestJS与MongoDB创建自定义API。我有以下设置:

  // users.controller.ts

  @Post('sign-up')
  @UseFilters(MongoExceptionFilter)
  async signUp(@Body() createUserDto: CreateUserDto): Promise<any> {
    return await this.userService.signUp(createUserDto).catch(error => {
      throw new BadRequestException(error);
    });
  }
  // user.service.ts

  async signUp(createUserDto: CreateUserDto): Promise<User> {
  const createUser = new this.userModel(createUserDto);
    return await createUser.save();
  }
  // mongo-exception.filter.ts

  import { ArgumentsHost,Catch, ConflictException, ExceptionFilter } from '@nestjs/common';
  import { MongoError } from 'mongodb';

  @Catch(MongoError)
  export class MongoExceptionFilter implements ExceptionFilter {
    catch(exception: MongoError, host: ArgumentsHost) {
      console.log('>>>>>>>>>>>>>>>>>>>> exception: ', exception);
    }
  }
  // package.json
  "dependencies": {
    "@nestjs/common": "^5.4.0",
    "@nestjs/core": "^5.4.0",
    "@nestjs/jwt": "^0.2.1",
    "@nestjs/mongoose": "^5.2.2",
    "@nestjs/passport": "^5.1.0",
    "@nestjs/typeorm": "^5.2.2",
    "fancy-log": "^1.3.3",
    "mongoose": "^5.4.7",
    "nestjs-config": "^1.3.0",
    "passport": "^0.4.0",
    "passport-http-bearer": "^1.0.1",
    "passport-jwt": "^4.0.0",
    "reflect-metadata": "^0.1.12",
    "rimraf": "^2.6.2",
    "rxjs": "^6.2.2",
    "typeorm": "^0.2.12",
    "typescript": "^3.0.1",
    "util": "^0.11.1"
  },

现在,无论何时我对/ sign-up路由进行POST调用,都应在save()中调用user.service.ts。所有这一切。接下来,当我再次发布/ sign-up路由时,它应该触发MongoDB错误,因为具有相同电子邮件地址的用户已经存在(电子邮件地址是唯一的,因此密钥重复)。我只在将错误记录在.catch(err => ...);中时看到该错误,但是问题是自定义MongoExceptionFilter。我不会触发MongoError。当我将@Catch()留为空白时,它会触发但无法处理异常。

我在做什么错?自从我看到this帖子并将其用作基础以来,我似乎无法使其正常工作。是Mongoose或NestJS的更新,为什么它不再起作用了?

1 个答案:

答案 0 :(得分:1)

这是因为您将所有错误都转换为BadRequestExceptions,因此您的MongoExceptionFilter不承担任何责任;它会检查instanceof MongoError返回BadRequestException为false的return await this.userService.signUp(createUserDto).catch(error => { throw new BadRequestException(error); });

.catch()

这发生在运行异常过滤器之前;他们总是跑到最后。


从您的控制器中删除@Catch() export class BadRequestFilter implements ExceptionFilter { catch(exception: Error, host: ArgumentsHost) { const response = host.switchToHttp().getResponse(); response.status(400).json({message: exception.message}); } } 。如果您真的想将所有其他异常转换为BadRequestExceptions(400),则可以编写第二个异常过滤器,该过滤器处理MongoExceptionFilter未处理的所有异常:

@Catch(MongoError)
export class MongoFilter implements ExceptionFilter {
  catch(exception: MongoError, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();
    if (exception.code === 11000) {
      response.status(400).json({ message: 'User already exists.' });
    } else {
      response.status(500).json({ message: 'Internal error.' });
    }
  }
}

@UseFilters(BadRequestFilter, MongoFilter)
async signUp(@Body() createUserDto: CreateUserDto): Promise<any> {

然后将两者都添加到您的控制器中(顺序很重要!):

/* Instantiate instantsearch.js */
var search = instantsearch({
    appId: algolia.application_id,
    apiKey: algolia.search_api_key,
    indexName: algolia.indices.searchable_posts.name,
    urlSync: {
        mapping: {'q': 's'},                                                                     
        trackedParameters: ['query']
},
searchParameters: {
    facetingAfterDistinct: true,
    highlightPreTag: '__ais-highlight__',
    highlightPostTag: '__/ais-highlight__'
                    }
                });

    /* Search box widget */
    search.addWidget(
        instantsearch.widgets.searchBox({
            container: '#algolia-search-box',
            placeholder: 'Search ...',
            searchAsYouType: false,
            wrapInput: false,
            poweredBy: algolia.powered_by_enabled
        })
    );

    /* Hits widget */
    search.addWidget(
        instantsearch.widgets.hits({
            container: '#algolia-hits',
            hitsPerPage: 10,
            templates: {
                empty: 'No results were found for "<strong>{{query}}</strong>".',
                item: wp.template('instantsearch-hit')
            },
            transformData: {
                item: function (hit) {

                    function replace_highlights_recursive (item) {
                      if( item instanceof Object && item.hasOwnProperty('value')) {
                          item.value = _.escape(item.value);
                          item.value = item.value.replace(/__ais-highlight__/g, '<em>').replace(/__\/ais-highlight__/g, '</em>');
                      } else {
                          for (var key in item) {
                              item[key] = replace_highlights_recursive(item[key]);
                          }
                      }
                      return item;
                    }

                    hit._highlightResult = replace_highlights_recursive(hit._highlightResult);
                    hit._snippetResult = replace_highlights_recursive(hit._snippetResult);

                    console.log(hit);

                    if ( hit.post_excerpt != '' ){
                        hit._snippetResult['content']['value'] = hit.post_excerpt;  
                    }
                    if ( hit.short_title.length > 0 && hit.short_title[0] != '' ){
                        hit._highlightResult['post_title']['value'] = hit.short_title[0];   
                    }

                    return hit;
                }
            }
        })
    );

    /* Pagination widget */
    search.addWidget(
        instantsearch.widgets.pagination({
            container: '#algolia-pagination'
        })
    );

    /* Tags refinement widget */
    search.addWidget(
        instantsearch.widgets.refinementList({
            container: '#facet-tags',
            attributeName: 'taxonomies.aar_article_type',
            operator: 'and',
            limit: 15,
            sortBy: ['isRefined:desc', 'count:desc', 'name:asc'],
            templates: {
                header: '<h2 class="widgettitle">Filter Results</h2>'
        }
    })
);

/* Start */
search.start();