Mongoose 自定义类型在 hapi / rest-hapi 中导致 500 内部服务器错误

Posted

技术标签:

【中文标题】Mongoose 自定义类型在 hapi / rest-hapi 中导致 500 内部服务器错误【英文标题】:Mongoose custom type causes 500 internal server error in hapi / rest-hapi 【发布时间】:2017-08-11 15:52:02 【问题描述】:

我在带有 rest-hapi 的 hapi 服务器中使用 Mongoose。根据http://mongoosejs.com/docs/customschematypes.html 的文档,我在Mongoose 中定义了一个自定义类型。在遇到我自己的自定义类型的问题后,我还尝试了文档的 Int8 示例。

对于自定义类型,无效值会导致 hapi 服务器遇到内部服务器错误,而不是返回 400 错误响应。使用内置字符串类型,无效值会导致预期的 400 错误响应。

我查看了 String 类型的实现,它的 cast 函数针对无效值抛出错误,就像我自定义类型中的 cast 函数一样。 (请注意,该示例有一条注释说要抛出 CastError,但实际上会抛出错误。我已经尝试了两种方法,结果相同。)

我需要怎么做才能使我的自定义类型的行为与无效值的内置类型相同?

这是一个演示问题的测试模型:

'use strict';

module.exports = function(mongoose) 
  function Int8(key, options) 
    mongoose.SchemaType.call(this, key, options, 'Int8');
  

  Int8.prototype = Object.create(mongoose.SchemaType.prototype);

  Int8.prototype.cast = function(val) 
    let _val = Number(val);
    if (isNaN(_val)) 
      throw new Error('Int8: ' + val + ' is not a number');
    
    _val = Math.round(_val);
    if (_val < -0x80 || _val > 0x7F) 
      throw new Error('Int8: ' + val +
                      ' is outside of the range of valid 8-bit ints');
    
    return _val;
  ;

  mongoose.Schema.Types.Int8 = Int8;

  const modelName = 'test';
  const Types = mongoose.Schema.Types;

  const Schema = new mongoose.Schema(
    string: 
      type: Types.String,
      required: true,
    ,
    int8: 
      type: Types.Int8,
      required: true,
    ,
  );

  Schema.statics = 
    collectionName: modelName,
    routeOptions: ,
  ;

  return Schema;
;

这是我的测试文件:

'use strict';

const expect = require('chai').expect;
const merge = require('lodash/merge');

describe.only('/test', () => 
  describe('POST', () => 
    const requestDefaults = 
      method: 'POST',
      url: '/test',
      payload: ,
    ;

    describe('invalid data', () => 
      it('should reject missing fields', () => 
        const request = merge(, requestDefaults);

        return server.inject(request)
          .then(response => 
            expect(response)
              .to.be.an('object')
              .with.property('statusCode', 400);
          );
      );

      it('should reject invalid string', () => 
        const request = merge(, requestDefaults, 
          payload: 
            string: 7,
            int8: 7,
          ,
        );

        return server.inject(request)
          .then(response => 
            expect(response)
              .to.be.an('object')
              .with.property('statusCode', 400);
          );
      );

      it('should reject invalid int8', () => 
        const request = merge(, requestDefaults, 
          payload: 
            string: 'hello',
            int8: 0xFF,
          ,
        );

        return server.inject(request)
          .then(response => 
            expect(response)
              .to.be.an('object')
              .with.property('statusCode', 400);
          );
      );
    );
  );
);

这是测试输出(堆栈跟踪已修整):

[10:34:16.983] 39140 LOG      api/mongoose — Connecting to Database...:
[10:34:16.991] 39140 LOG      api/mongoose —    URI: `mongodb://127.0.0.1:9001/test`
[10:34:17.013] 39140 LOG      api — Initializing Server...


(node:39140) DeprecationWarning: `open()` is deprecated in mongoose >= 4.11.0, use `openUri()` instead, or set the `useMongoClient` option if using `connect()` or `createConnection()`. See http://mongoosejs.com/docs/connections.html#use-mongo-client
  /test
    POST
      invalid data
        ✓ should reject missing fields (95ms)
        ✓ should reject invalid string
[10:34:18.151] 39140 LOG      api/test/Create — params(), query(), payload("string":"hello","int8":255)
[10:34:18.181] 39140 ERROR    ../../rest-hapi/utilities/handler-helper.js:464:23 api/test/Create — There was an error creating the resource.
[10:34:18.183] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:27:11 api/test/Create — MongooseError: 
[10:34:18.183] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:27:11 api/test/Create —     at ValidationError (/Users/chipmunk/git/magic-wand-api/node_modules/mongoose/lib/error/validation.js:27:11)
[10:34:18.183] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:27:11 api/test/Create —     at model.Document.invalidate (/Users/chipmunk/git/magic-wand-api/node_modules/mongoose/lib/document.js:1609:32)
[10:34:18.183] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:27:11 api/test/Create —     at model.Document.set (/Users/chipmunk/git/magic-wand-api/node_modules/mongoose/lib/document.js:758:10)
...
[10:34:18.186] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:60:11 api/test/Create — TypeError: Boom.serverTimeout is not a function
[10:34:18.186] 39140 ERROR    ../../rest-hapi/utilities/error-helper.js:60:11 api/test/Create —     at Object.formatResponse (/Users/chipmunk/git/magic-wand-api/node_modules/rest-hapi/utilities/error-helper.js:43:27)
...
        1) should reject invalid int8


  2 passing (1s)
  1 failing

  1) /test POST invalid data should reject invalid int8:

      AssertionError: expected  Object (raw, headers, ...)  to have property 'statusCode' of 400, but got 500
      + expected - actual

      -500
      +400

      at test/api/test.js:54:21
      at <anonymous>
      at process._tickDomainCallback (internal/process/next_tick.js:208:7)



error Command failed with exit code 1.1

【问题讨论】:

【参考方案1】:

我找到了问题的答案。

rest-hapi 在将文档传递给 Mongoose 之前使用 Joi 验证文档。 rest-hapi 通过将 Mongoose 类型映射到相应的 Joi 类型,将 Mongoose 模型转换为 Joi 模型。当然,它不能识别我的自定义类型,所以它让 Joi 接受该字段的任何值。

因此,对于具有标准 String 类型的字段,rest-hapi 在初始验证步骤中拒绝无效值并返回 400 错误响应。对于具有自定义 Mongoose 类型的字段,rest-hapi 在初始验证步骤中接受它,但 mongoose 拒绝它,rest-hapi 返回 500 错误响应。

我还没有考虑如何解决这种行为差异。

【讨论】:

以上是关于Mongoose 自定义类型在 hapi / rest-hapi 中导致 500 内部服务器错误的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose Schema 类型作为 TypeScript 的自定义接口?

Typegoose / Mongoose 将自定义类型映射到 db

用于自定义序列类型的 Python re

Mongoose 的自定义错误消息

Mongoose:简单的自定义验证功能不起作用

在 Mongoose 中定义变体类型