Node.js hapi单页应用程序

11

我有一个单页面应用(NodeJS),想从Express迁移到Hapi。通常做法是将静态文件服务,其余所有路由指向包含AngularJS应用和路由配置的单个页面。

// Express routing, first the static files
app.use( express.static(__dirname + '/public') );

// Second the api routes
app.get('/api', function(req, res){
    res.send( {api: 'response' } )
});

// Finally everything else maps to the single page app:
app.get('*', function(req, res){
    res.sendfile('./public/html/controllers.index.html')
});
在HapiJS中,我不知道如何复制相同的代码(而不使用express.static中间件),因为:
Hapi = require('hapi');
var server =  new Hapi.Server('localhost', 84);

server.route({
    method: 'GET',
    path: '/{p*}',
    handler: function (request, reply) {
        reply.file('public/html/index.html');
    }
});
在上面的代码中,无论何种请求都将映射到我的单个页面('public / html / index.html'),但如果我这样做,则js、css、jpg和文件将被映射到同一文件而不是脚本、样式和图像(对'/images/bg.png'的请求将下载单个页面而不是图像文件)。
我知道如果我将路径“/'”设置为我的单个页面,然后将“{p *}”设置为“{directory: {path:'/public'}}”,那么我将拥有所需的行为,但有一个问题,如果某些用户复制并粘贴特定的url(比如'/account/login'),然后按enter键,该路由将在HapiJS中映射,并且响应将是'Not Found(404)',angular路由将永远无法响应。
有人知道如何解决这个问题吗?
问题的关键部分是:
  • 仅使用HapiJS(没有express或其他中间件)
  • 不要路由每个angular路由(只路由其他未路由到单个页面son的内容,因此angular可以处理路由)
6个回答

7

不确定这是否能帮到你,但是你的起始代码在Hapi.js中有些“奇怪”。以下是我用于设置简单Hapi.js SPA的方法。

如果你想使用特定的URL,例如“Account/Login”,则必须将路径指向该特定部分。(路径:'/account/login')

区别在于我将整个目录(包括不同的文件)指向,而你仅回复index.html文件。列表参数允许你在URL中决定是否显示目录结构。默认为false。

更多信息请参见:http://hapijs.com/tutorials/serving-files#directory-handler

var Hapi = require('hapi');
var Path = require('path');

var server = new Hapi.Server(8080, 'localhost');


server.route({
    method: 'GET',
    path: '/{path*}',
    handler: {
   directory: {
            path: './public',
            listing: false,
            index: true
        }
    }      
});



server.start(function(){
 console.log('server started');
});


其实并不是(将近一年后), 每个404请求必须在其内容中响应index.html,除了index.html之外的唯一响应应该是实际存在的文件。*页面路由由angularjs处理,如果路由在angular中不存在,则angular会显示404错误。 - Amílcar Calles
1
这不处理客户端路由。也就是说,当页面刷新时,您将会得到404错误。 - Tony

5

受到 inert 文档 的启发,我通过从静态目录中提供所有内容来解决这个问题。然后,我添加了一个 onPostHandler,在即将返回404时返回索引文件。您的客户端路由器随后可以将重定向发送到静态文件目录中的现有 404.html 文件。

// Static Assets
server.route({
  method: 'GET',
  path: '/{param*}',
  handler: {
    directory: {
      path: ['app/static'],
      listing: false,
      index: ['index.html']
    }
  }
});

// return index.html for everything else
server.ext('onPostHandler', (request, reply) => {
  console.log('WORD');
  const response = request.response;
  if (response.isBoom && response.output.statusCode === 404) {
    return reply.file('app/static/index.html');
  }
  return reply.continue();
});

这个对我有用。看起来是一个常见问题的解决方法,所以我不确定它是否是最干净的解决方案。 - Tdy

2

这个回答是无效的。正如@Al jey在评论中所说,Hapi有自己的算法来排序路由,请不要遵循此答案。


类似于expressJS,Hapi通过顺序处理路由。只需按优先级定义路由顺序:

server.route(
{    // Angular/API Route
    method: 'GET',
    path: '/api',
    handler: function (request, reply) {
         reply( {api: 'response' } )
    }
});

server.route({    // Other assets If you have
    method: 'GET',
    path: '/assets/{param*}',
    handler: {
    directory: {
        path: './assets',
        listing: false,
        index: true
    }
});

server.route({   // Everything else 
    method: 'GET',
    path: '/{p*}',
    handler: function (request, reply) {
        reply.file('public/html/index.html');
    }
});

1
这实际上是完全错误的,HapiJS通过它们的特定性处理路由,定义顺序完全不相关,不幸的是... - Eugene Kuzmenko
@Al jey,是的,你说得对。在我挖掘了一番后,我发现它们有自己的顺序。我现在正在编辑答案。 - Hereblur

1
我曾经按照这里的描述成功使用了惰性加载。如果您向下滚动,您可以找到“目录处理程序”部分。
我的解决方案如下:
var Hapi = require('hapi');
var Path = require('path');

var server = new Hapi.Server();
server.connection({ 
  host: 'localhost',
  port: 8001 
});

server.register(require('inert'), (err) => {

  if (err) {
    throw err;
  }

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


  server.start((err) => {

    if (err) {
      throw err;
    }

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

1

确保你安装了inert插件。

在你的server.js文件(或任何你命名的文件)中,配置相对路径以提供静态文件,请参见下面的内容。

const server = new Hapi.Server({
  connections: {
    routes: {
      cors: true,
      files: {
        relativeTo: Path.join(__dirname, 'public')
      }
    },
    router: {
      stripTrailingSlash: true
    }
  }
})

然后为您的路由,注册一个新的路由插件,就像下面这样。这是假设您的入口index.html(对于React或Angular)在如上配置的public目录中。

exports.register = (server, options, next) => {
  server.route([{
    method: 'GET',
    path: '/{param*}',
    handler: {
      directory: {
        path: '.',
        redirectToSlash: true,
        listing: false,
        index: true
      }
    }
  }])
  server.ext('onPostHandler', (req, res) => {
    const response = req.response
    if (response.isBoom && response.output.statusCode >= 404) {
      return res.file('index.html')
    }
    return res.continue()
  })
  next()
}

exports.register.attributes = {
  name: 'static-route',
  version: '1.0.0'
}

现在每当HapiJS抛出404错误时,它会被转发到您的React / Angular应用程序,如果存在该路由,则可以处理该路由。

如果在此基础上还有一个API路由,会发生什么情况呢?我尝试了所有的路由,包括API路由,现在所有的路由都会被重定向回去。 - WABBIT0111
这个配置仅处理404 +错误,因此,如果您有API,则不应重定向,除非您的服务器抛出404 +错误(包括500 +错误)。 - Dee S
你可以将检查状态码是否大于等于404的那一行改为仅检查404错误,例如 if (response.isBoom && response.output.statusCode === 404)。希望这能帮到你。 - Dee S
抱歉,它没有提供任何API路由。您可以在此处查看我的示例GitHub代码:https://github.com/7seven7lst/hapi-inert-test 或者在StackOverflow上查看问题:https://stackoverflow.com/questions/44866806/hapi-single-page-application-using-inert-plugin-doesnt-serve-up-api-route - WABBIT0111

0

如果您正在使用Hapi@18和Inert,请注意以下内容。

  const server = Hapi.server({
    port: 3000,
    routes: {
      files: {
        relativeTo: Path.join(__dirname, 'build'),
      }
    }
  });

  server.route({
    method: 'GET',
    path: '/static/{param?}',
    handler: {
      directory: {
        path: '.',
        redirectToSlash: true,
      }
    }
  });

  server.route({
    method: 'GET',
    path: '/{any*}',
    handler: {
      file: 'index.html'
    }
  });

在这里,我的 ./build 包含所有的JS资源和 index.html 文件。 index.html 请求入口文件 ./build/main.js 位于 /static/main.js 路径下。其余的脚本文件是相对于彼此的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接