服务器方法 #

一、方法概述 #

1.1 什么是服务器方法? #

服务器方法是附加到服务器实例上的函数,可以在整个应用中共享和复用。

1.2 基本定义 #

javascript
server.method('formatDate', (date) => {
    return new Date(date).toISOString();
});

server.route({
    method: 'GET',
    path: '/date',
    handler: (request, h) => {
        return server.methods.formatDate(new Date());
    }
});

二、定义方法 #

2.1 简单方法 #

javascript
server.method('greet', (name) => {
    return `Hello, ${name}!`;
});

server.route({
    method: 'GET',
    path: '/greet/{name}',
    handler: (request, h) => {
        return server.methods.greet(request.params.name);
    }
});

2.2 异步方法 #

javascript
server.method('getUser', async (id) => {
    const user = await User.findById(id);
    return user;
});

server.route({
    method: 'GET',
    path: '/users/{id}',
    handler: async (request, h) => {
        return await server.methods.getUser(request.params.id);
    }
});

2.3 带选项的方法 #

javascript
server.method({
    name: 'calculate',
    method: (a, b, operation = 'add') => {
        switch (operation) {
            case 'add': return a + b;
            case 'subtract': return a - b;
            case 'multiply': return a * b;
            case 'divide': return a / b;
            default: throw new Error('Unknown operation');
        }
    }
});

三、方法缓存 #

3.1 启用缓存 #

javascript
server.method('getUser', async (id) => {
    console.log('Fetching user from database');
    return await User.findById(id);
}, {
    cache: {
        expiresIn: 60 * 1000,
        generateTimeout: 100
    }
});

3.2 缓存配置选项 #

javascript
server.method('getProduct', async (id) => {
    return await Product.findById(id);
}, {
    cache: {
        cache: 'redis-cache',
        expiresIn: 60 * 60 * 1000,
        staleIn: 30 * 60 * 1000,
        staleTimeout: 100,
        generateTimeout: 1000
    }
});

3.3 缓存键生成 #

javascript
server.method('search', async (query, filters) => {
    return await SearchService.search(query, filters);
}, {
    cache: {
        expiresIn: 60 * 1000,
        generateTimeout: 100
    },
    generateKey: (query, filters) => {
        return `${query}:${JSON.stringify(filters)}`;
    }
});

四、方法命名空间 #

4.1 命名空间方法 #

javascript
server.method('user.getById', async (id) => {
    return await User.findById(id);
});

server.method('user.getByEmail', async (email) => {
    return await User.findByEmail(email);
});

server.method('user.create', async (data) => {
    return await User.create(data);
});

4.2 使用命名空间方法 #

javascript
server.route({
    method: 'GET',
    path: '/users/{id}',
    handler: async (request, h) => {
        return await server.methods.user.getById(request.params.id);
    }
});

五、方法绑定 #

5.1 绑定上下文 #

javascript
server.method('format', function (value) {
    return `${this.prefix}${value}${this.suffix}`;
}, {
    bind: {
        prefix: '[',
        suffix: ']'
    }
});

六、完整方法配置 #

6.1 配置对象 #

javascript
server.method({
    name: 'userService.getProfile',
    method: async function (userId) {
        const user = await this.User.findById(userId);
        const posts = await this.Post.findByUserId(userId);
        return { user, posts };
    },
    options: {
        bind: {
            User: UserModel,
            Post: PostModel
        },
        cache: {
            expiresIn: 60 * 1000,
            generateTimeout: 100
        },
        generateKey: (userId) => `profile:${userId}`
    }
});

七、批量定义 #

7.1 定义多个方法 #

javascript
server.method([
    {
        name: 'math.add',
        method: (a, b) => a + b
    },
    {
        name: 'math.subtract',
        method: (a, b) => a - b
    },
    {
        name: 'math.multiply',
        method: (a, b) => a * b
    },
    {
        name: 'math.divide',
        method: (a, b) => a / b
    }
]);

八、实际应用 #

8.1 数据库查询 #

javascript
server.method('db.findUser', async (id) => {
    const result = await server.app.db.query(
        'SELECT * FROM users WHERE id = $1',
        [id]
    );
    return result.rows[0];
}, {
    cache: {
        expiresIn: 60 * 1000,
        generateTimeout: 100
    }
});

8.2 外部API调用 #

javascript
const Wreck = require('@hapi/wreck');

server.method('api.getWeather', async (city) => {
    const { payload } = await Wreck.get(
        `https://api.weather.com/${city}`
    );
    return JSON.parse(payload);
}, {
    cache: {
        expiresIn: 10 * 60 * 1000,
        generateTimeout: 5000
    }
});

8.3 计算密集型任务 #

javascript
server.method('calculateStats', (data) => {
    let sum = 0;
    for (let i = 0; i < data.length; i++) {
        sum += data[i];
    }
    return {
        sum,
        avg: sum / data.length,
        count: data.length
    };
}, {
    cache: {
        expiresIn: 60 * 60 * 1000,
        generateTimeout: 10000
    },
    generateKey: (data) => {
        return `stats:${data.length}:${data[0]}:${data[data.length - 1]}`;
    }
});

九、缓存策略 #

9.1 配置缓存 #

javascript
const server = Hapi.server({
    port: 3000,
    cache: [
        {
            name: 'memory-cache',
            provider: {
                constructor: require('@hapi/catbox-memory'),
                options: {
                    maxByteSize: 100 * 1024 * 1024
                }
            }
        },
        {
            name: 'redis-cache',
            provider: {
                constructor: require('@hapi/catbox-redis'),
                options: {
                    partition: 'my-app',
                    host: 'localhost',
                    port: 6379
                }
            }
        }
    ]
});

9.2 使用指定缓存 #

javascript
server.method('getHotData', async (key) => {
    return await fetchHotData(key);
}, {
    cache: {
        cache: 'memory-cache',
        expiresIn: 30 * 1000
    }
});

server.method('getColdData', async (key) => {
    return await fetchColdData(key);
}, {
    cache: {
        cache: 'redis-cache',
        expiresIn: 24 * 60 * 60 * 1000
    }
});

十、缓存失效 #

10.1 手动清除缓存 #

javascript
server.route({
    method: 'PUT',
    path: '/users/{id}',
    handler: async (request, h) => {
        const user = await User.update(request.params.id, request.payload);
        
        await server.methods.getUser.cache.drop(request.params.id);
        
        return user;
    }
});

10.2 自动更新 #

javascript
server.method('getSettings', async () => {
    return await Settings.findAll();
}, {
    cache: {
        expiresIn: 60 * 1000,
        staleIn: 30 * 1000,
        staleTimeout: 100,
        generateTimeout: 1000
    }
});

十一、完整示例 #

11.1 用户服务 #

javascript
const Hapi = require('@hapi/hapi');
const CatboxRedis = require('@hapi/catbox-redis');

const init = async () => {
    const server = Hapi.server({
        port: 3000,
        cache: [{
            name: 'redis',
            provider: {
                constructor: CatboxRedis,
                options: {
                    partition: 'my-app',
                    host: 'localhost',
                    port: 6379
                }
            }
        }]
    });
    
    server.method('user.getById', async (id) => {
        console.log('Fetching user from database');
        return await User.findById(id);
    }, {
        cache: {
            cache: 'redis',
            expiresIn: 60 * 60 * 1000,
            generateTimeout: 100
        },
        generateKey: (id) => `user:${id}`
    });
    
    server.method('user.getByEmail', async (email) => {
        return await User.findByEmail(email);
    }, {
        cache: {
            cache: 'redis',
            expiresIn: 60 * 60 * 1000,
            generateTimeout: 100
        }
    });
    
    server.method('user.create', async (data) => {
        const user = await User.create(data);
        return user;
    });
    
    server.route([
        {
            method: 'GET',
            path: '/users/{id}',
            handler: async (request, h) => {
                return await server.methods.user.getById(request.params.id);
            }
        },
        {
            method: 'POST',
            path: '/users',
            handler: async (request, h) => {
                const user = await server.methods.user.create(request.payload);
                return h.response(user).code(201);
            }
        }
    ]);
    
    await server.start();
    console.log('Server running on %s', server.info.uri);
};

init();

十二、总结 #

服务器方法要点:

功能 说明
server.method() 定义方法
cache 方法缓存
generateKey 缓存键生成
bind 绑定上下文
命名空间 方法分组

下一步,让我们学习缓存策略!

最后更新:2026-03-28