使用 Hapi 响应路由

React routing with Hapi

我需要将 Hapi 与 create-react-app 一起使用,其中 Hapi 充当 api 请求的代理并且还服务于 React 应用程序。

我正在尝试让路由正常工作,但它似乎不适用于当前的 Hapi 配置。

这是我的服务器代码:

const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');

const init = async () => {

    const server = new Hapi.Server({
        port: process.env.PORT || 5000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, '../build')
            }
        }
    });

    await server.register(Inert);

    server.route({
        method: 'GET',
        path: '/{param*}',
        handler: {
            directory: {
                path: '.'
            }
        }
    });

    const options = {
        ops: {
            interval: 1000
        },
        reporters: {
            myConsoleReporter: [
                {
                    module: 'good-console',
                    args: [{ request: '*', response: '*' }]
                },
                'stdout'
            ]
        }
    };

    await server.register({
        plugin: require('good'),
        options,
    });

    await server.start();

    console.log('Server running at:', server.info.uri);
};

init();

index.html 文件在 localhost:5000 打开时加载正常。我在 react-router 部分配置了路由 /dashboard 。点击 localhost:5000/dashboard 给出 404。

问题:

注意:当 运行 带有 npm start 的 React 应用程序时路由有效。但这是没有 Hapi 服务器 运行.

我是使用 Hapi 的新手,因此不胜感激。

所以我尝试了各种 hapi+inert 组合,这就是最终对我有用的东西。

server.js

const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const routes = require('./routes');
const init = async () => {

    console.log('Routes are', routes);
    const server = new Hapi.Server({
        port: process.env.PORT || 5000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, '../build')
            }
        }
    });

    await server.register(Inert);

    server.route(routes);

    /**
     * This is required here because there are references to *.js and *.css in index.html,
     * which will not be resolved if we don't match all remaining paths.
     * To test it out, comment the code below and try hitting /login.
     * Now that you know it doesn't work without the piece of code below,
     * uncomment it.
     */
    server.route({
        method: 'GET',
        path: '/{path*}',
        handler: {
            directory: {
                path: '.',
                redirectToSlash: true,
                index: true,
            }
        }
    });

    const options = {
        ops: {
            interval: 1000
        },
        reporters: {
            myConsoleReporter: [
                {
                    module: 'good-console',
                    args: [{ request: '*', response: '*' }]
                },
                'stdout'
            ]
        }
    };

    await server.register({
        plugin: require('good'),
        options,
    });

    await server.start();

    console.log('Server running at:', server.info.uri);
};

init();

/routes/index.js

/**
* Use this for all paths since we just need to resolve index.html for any given path.
* react-router will take over and show the relevant component.
* 
* TODO: add a 404 handler for paths not defined in react-router
*/
const fileHandler = {
    handler: (req, res) => {
        console.log(res.file('index.html'));
        return res.file('index.html');
    }
}

const routes = [
    { method: 'GET', path: '/login', config: fileHandler },
]

module.exports = routes;

这里要注意的关键是,对于任何命名路径(在本例中为 /login),我们总是 return index.html 文件。对于所有其他路径,我们告诉 hapi 从我们的 build 目录到 return 文件,这样我们 index.hml 中对 *.css*.js 文件的任何引用都将是已解决,我们不会遇到 404。

我不确定 react-router 如何在加载 index.html 后接管路径解析,但这超出了这个问题的范围,可能是另一个讨论的话题。

关于hot-reload的第二个问题,我还在摸索中。现在我 运行 hapi 服务器和 react-app 都是独立的,因为我需要 /api 在 react-app 中使用。欢迎任何建议或答案。

这是我的做法。测试了它。版本 "@hapi/hapi": "^20.0.1".

const path = require("path")
const Hapi = require('@hapi/hapi')
const Boom = require('@hapi/boom');

const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0',
    routes: {
        files: {
            relativeTo: path.join(__dirname, 'YOU BUILD REACT DIR')
        }

    }
});
(async () => {
    await server.register([
        require('vision'),
        require('inert')
    ]);
    server.route(
        [{
            method: 'GET',
            path: '/{path*}',
            options: {
                ext: {
                    onPreResponse: {
                        method(req, h) {
                            //for other path prefix /Api
                            const isApi = req.path.substr(1)
                                .toLowerCase()
                                .trim()
                                .split('/')[0]
                                .replace(/\//g, "") === "api"

                            const response = req.response
                            if (response && req.response.output && req.response.output.statusCode === 404) {
                                if (isApi)
                                    return Boom.notFound("Not Found")
                                return h.file('index.html');
                            }
                            return h.continue
                        },
                    }
                }
            },
            handler: {
                directory: {
                    path: ".",
                    listing: false,
                    index: true
                }
            }
        },
        {
            method: 'GET',
            path: '/Api/test',
            handler: () => "OK"
        }
    ])
    await server.start();
    console.log('Server running on %s', server.info.uri)
})()