Servindo arquivos estáticos

Este tutorial é compatível com hapi v16

Inevitavelmente quando desenvolvemos algumas aplicações web, surge a necessidade de servir simples arquivos do disco. Há um plugin chamado inert que adiciona essa funcionalidade para o hapi atráves de manipuladores adicionais.

Primeiro você precisa instalar e adicinar o inert como uma dependência para seu projeto:

npm install --save inert

reply.file()

Primeiro, para usar o método reply:

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

    if (err) {
        throw err;
    }

    server.route({
        method: 'GET',
        path: '/picture.jpg',
        handler: function (request, reply) {
            reply.file('/path/to/picture.jpg');
        }
    });

    server.start((err) => {

        if (err) {
            throw err;
        }

        console.log('Servidor rodando em:', server.info.uri);
    });
});

Como eu tenho certeza que você adivinhou, em sua forma mais simples você passa um caminho para reply.file() e está feito.

Caminhos relativos

Para simplificar as coisas, especialmente se você tiver várias rotas que respondem com arquivos, você pode configurar um caminho base no seu servidor e passar apenas os caminhos relativos para reply.file()

'use strict';

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

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

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

    if (err) {
        throw err;
    }

    server.route({
        method: 'GET',
        path: '/picture.jpg',
        handler: function (request, reply) {
            reply.file('path/to/picture.jpg');
        }
    });

    server.start((err) => {

        if (err) {
            throw err;
        }

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

Como você pode ter adivinhado pela opção passada para o servidor, o parâmetro relativeTo também pode ser definido em um nível por conexão ou por rota.

Manipulador de arquivo

Uma alternativa para a rota acima seria o uso do manipulador file:

server.route({
    method: 'GET',
    path: '/picture.jpg',
    handler: {
        file: 'picture.jpg'
    }
});

Opções do manipulador de arquivo

Nós também podemos especificar o parâmetro como uma função que aceita o objeto request e retorna uma string que representa o caminho do arquivo (absoluto ou relativo):

server.route({
    method: 'GET',
    path: '/{filename}',
    handler: {
        file: function (request) {
            return request.params.filename;
        }
    }
});

Isso também pode ser um objeto com a propriedade path. Ao usar o objeto formulário do manipulador, nós podemos fazer algumas coisas adicionais, como o ajuste do cabeçalho Content-Disposition e permitir arquivos compactados, como:

server.route({
    method: 'GET',
    path: '/script.js',
    handler: {
        file: {
            path: 'script.js',
            filename: 'client.js', // sobreescreve o nome do arquivo no cabeçalho Content-Disposition
            mode: 'attachment', // especifica o Content-Disposition com um anexo
            lookupCompressed: true // permite olhar para script.js.gz se a requisição permitir isso
        }
    }
});

Diretório de manipuladores

Além do manipulador file, inert também adiciona um manipulador de directory que permite que você especifique um caminho para servir multiplos arquivos. Para usá-lo, você deve especificar um caminho como um parâmetro. O nome do paramâmetro não importa, no entanto. Você pode usar a extensão asterísco para restringir a profundidade do arquivo também. O uso mais básico do manipulador de diretório se parece com:

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

Opções do manipulador de diretório

A rota acima responderá qualquer requisição procurando por um arquivo correspondente no diretório public. Note que a requisição para / nessa configuração, responderá com um HTTP 403 por que, por padrão, o manipulador não permitirá uma lista de arquivos. Você pode alterar isso definindo a propriedade listing para true, assim:

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

Agora a requisição para \ responderá com um HTML exbindo com o conteúdo do diretório. Nós podemos aproveitar esse servidor estático e ir um passo adiante definindo a opção index para true, o que siginifica que uma requisição para / vai tentar carregar primeiro index.html. A opção index também aceita uma string ou um array de strings para especificar o arquivo(s) padrão. Por definição, a opção index para ['index.html', 'default.html'], uma requisição para / primeiramente tenta carregar /index.html e então default.html. Isso nos dá, em uma rota, um servidor web estático muito básico e simples.

Quando usamos um manipulador de diretório com a listagem habilitada, por padrão, arquivos ocultos não serão exibidos na lista. Isso pode ser alterado definindo a opção showHidden para true. Como o manipulador de arquivo, o manipulador de diretório também tem uma opção lookupCompressed para servir arquivos pré compactados, quando possível. Você também pode definir um defaultExtension que será anexado as requisições se o caminho do arquivo original não for encontrado. Isso significa que a requisição para /bacon também tentará o arquivo /bacon.html.