code: function wrapObject(obj, path, prefix) {
    const keys = Object.keys(obj);
    const ret = {};
    for (const key of keys) {
      if (is.function(obj[key])) {
        const names = utility.getParamNames(obj[key]);
        if (names[0] === 'next') {
          throw new Error(`controller \`${prefix || ''}${key}\` should not use next as argument from file ${path}`);
        }
        ret[key] = functionToMiddleware(obj[key]);
        ret[key][FULLPATH] = `${path}#${prefix || ''}${key}()`;
      } else if (is.object(obj[key])) {
        ret[key] = wrapObject(obj[key], path, `${prefix || ''}${key}.`);
      }
    }
    return ret;
  
    function functionToMiddleware(func) {
      const objectControllerMiddleware = async function(...args) {
        if (!this.app.config.controller || !this.app.config.controller.supportParams) {
          args = [ this ];
        }
        return await utils.callFn(func, args, this);
      };
      for (const key in func) {
        objectControllerMiddleware[key] = func[key];
      }
      return objectControllerMiddleware;
    }
  }
docs: wrap the method of the object, method can receive ctx as it's first argument