web-dev-qa-db-fra.com

Comment Express et hapi se comparent-ils?

Du point de vue de la conception et du développement d’applications Web, en quoi Express et Hapi se comparent-ils? Pour des exemples de base, ils semblent similaires, mais je voudrais en savoir plus sur les principales différences dans la structure globale des applications.

Par exemple, pour autant que je sache, Hapi utilise un mécanisme de routage différent qui ne prend pas en compte l’ordre d’enregistrement, permet des recherches plus rapides, mais est limité par rapport à Express. Y a-t-il d'autres différences importantes?

Il existe également un article sur le choix de Hapi (plutôt que Express) pour le développement du nouveau site Web npmjs.com. Cet article précise que "le système de plug-ins de Hapi signifie que nous pouvons isoler les différentes facettes et services de pour les microservices futurs. Express, en revanche, nécessite un peu plus de configuration pour obtenir la même fonctionnalité ", que signifie-t-il exactement?

118
Ali Shakiba

Mon organisation part avec Hapi. C'est pourquoi nous l'aimons.

Hapi c'est:

  • Soutenu par le corps majeur. Cela signifie que le soutien de la communauté sera fort et vous accompagnera tout au long des prochaines versions. Il est facile de trouver des passionnés Hapi et il existe de bons tutoriels (mais pas aussi nombreux et aussi tentaculaires que les tutoriels ExpressJs). À compter de cette date, npm et Walmart utilisent Hapi.
  • Il peut faciliter le travail d'équipes distribuées travaillant sur diverses parties des services back-end sans avoir à avoir une connaissance complète du reste de la surface de l'API (l'architecture de plugins de Hapi est la quintessence de cette qualité).
  • Laissez le framework faire ce qu’un framework est supposé: configurer les choses. Après cela, le cadre devrait être invisible et permettre aux développeurs de concentrer leur véritable énergie créative sur la construction de la logique commerciale. Après avoir utilisé Hapi pendant un an, je sens vraiment que Hapi accomplit cela. Je suis heureux!

Si vous souhaitez entendre directement Eran Hammer (responsable de Hapi)

Au cours des quatre dernières années, le hapi est devenu le cadre de choix de nombreux projets, grands ou petits. Ce qui rend hapi unique, c'est sa capacité à s'adapter aux grands déploiements et aux grandes équipes. Au fur et à mesure que le projet se développe, il en va de même de sa complexité - complexité de l'ingénierie et des processus. L’architecture et la philosophie de hapi gèrent la complexité croissante sans qu'il soit nécessaire de refactoriser constamment le code [lire la suite]

Commencer avec Hapi ne sera pas aussi facile que ExpressJs, car Hapi n’a pas le même "pouvoir de star" ... mais une fois que vous vous sentirez à l’aise, vous aurez BEAUCOUP de kilométrage. Cela m'a pris environ 2 mois en tant que nouveau pirate informatique qui utilisait irresponsablement ExpressJs pendant quelques années. Si vous êtes un développeur backend expérimenté, vous saurez lire la documentation et vous ne le remarquerez probablement même pas.

Les domaines dans lesquels la documentation Hapi peut s’améliorer:

  1. comment authentifier les utilisateurs et créer des sessions
  2. traitement des demandes d'origine croisée (CORS)
  3. téléchargement de fichiers (en plusieurs parties, chunked)

Je pense que l'authentification serait la partie la plus difficile, car vous devez décider quelle stratégie d'authentification utiliser (authentification de base, cookies, jetons JWT, OAuth). Bien que, techniquement, le problème de Hapi ne soit pas lié au fait que le paysage des sessions/authentification est si fragmenté ... mais je souhaite qu’ils fournissent un peu de contrôle pour cela. Cela augmenterait considérablement le bonheur des développeurs.

Les deux autres ne sont pas vraiment difficiles, les documents pourraient être écrits un peu mieux.

48
williamle8300

Faits en bref sur Hapi ou pourquoi Hapi JS? 

Hapi est centré sur la configurationIl dispose d'une authentification et d'une autorisation intégrées dans le frameworkIl a été publié dans une atmosphère éprouvée et a véritablement fait ses preuves Tous les modules ont une couverture de test à 100%. enregistre le plus haut niveau d'abstraction en dehors du noyau HTTP Facilement compréhensible via l'architecture de plugin

Hapi est un meilleur choix en termes de performances Hapi utilise un mécanisme de routage différent, qui peut effectuer des recherches plus rapidement, et prendre en compte l'ordre d'enregistrement en compte . Néanmoins, il est assez limité par rapport à Express. Et grâce au système de plugin Hapi, c'est possible d'isoler les différentes facettes et services qui aideraient l'application de nombreuses façons dans le futur.

Utilisation

Hapi est le framework préféré par rapport à Express. Hapi est principalement utilisé pour les applications d'entreprise à grande échelle.

Pourquoi les développeurs ne choisissent-ils pas Express lors de la création d'applications d'entreprise?

Les itinéraires sont plus difficiles à composer dans Express

Le middleware gêne la plupart du temps; chaque fois que vous définissez les itinéraires, vous devez écrire autant de nombres de codes.

Hapi serait le meilleur choix pour un développeur cherchant à créer une API RESTful. Hapi possède une architecture de micro-services et il est également possible de transférer le contrôle d'un gestionnaire à un autre en fonction de certains paramètres. Avec le plugin Hapi, vous pouvez profiter d'un niveau d'abstraction plus élevé autour de HTTP, car vous pouvez diviser la logique métier en éléments facilement gérables.

Un autre avantage important de Hapi est qu’il fournit des messages d’erreur détaillés en cas de mauvaise configuration. Hapi vous permet également de configurer la taille de téléchargement de votre fichier par défaut. Si la taille de téléchargement maximale est limitée, vous pouvez envoyer un message d'erreur à l'utilisateur pour lui indiquer que la taille du fichier est trop grande. Cela protégerait votre serveur contre les pannes, car les fichiers téléchargés n'essaieront plus de mettre en mémoire tampon tout un fichier.

  1. Tout ce que vous pouvez réaliser avec express peut également être facilement réalisé avec hapi.js.

  2. Hapi.js est très stylé et organise très bien le code. Si vous voyez comment le routage fonctionne et met la logique de base dans les contrôleurs, vous allez adorer.

  3. Hapi.js propose officiellement plusieurs plugins exclusivement pour hapi.js, de l’authentification basée sur les jetons à la gestion de session, et bien d’autres, Qui est une publicité. Cela ne signifie pas que le npm traditionnel ne peut pas être utilisé, ils sont tous supportés par hapi.js 

  4. Si vous codez dans hapi.js, un code serait très maintenable.

1
user9961607

J'ai commencé à utiliser Hapi récemment et j'en suis assez content. Mes raisons sont

  1. Plus facile à tester. Par exemple:

    • server.inject vous permet d'exécuter l'application et d'obtenir une réponse sans la lancer ni l'écouter.
    • server.info donne l'URI actuel, le port, etc.
    • server.settings accède à la configuration, par exemple. server.settings.cache obtient le fournisseur de cache actuel
    • en cas de doute, consultez les dossiers /test de n'importe quelle partie de l'application ou des plugins pris en charge pour voir des suggestions sur la façon de simuler/tester/stub, etc.
    • mon sentiment est que le modèle architectural de hapi vous permet de faire confiance mais de vérifier par exemple. Est-ce que mes plugins sont enregistrés ? Comment déclarer une dépendance de module ?
  2. Cela fonctionne hors de la boîte par exemple. fichiers téléchargés , renvoient des flux à partir de points de terminaison, etc.

  3. Les plugins essentiels sont conservés avec la bibliothèque principale. Par exemple, l'analyse de modèles , la mise en cache etc. L'avantage supplémentaire est que les mêmes normes de codage sont appliquées aux éléments essentiels.

  4. Erreurs saines et gestion des erreurs. Hapi valide les options de configuration et conserve une table de routage interne pour éviter les doublons. Ceci est très utile lors de l’apprentissage car les erreurs sont générées plus tôt que les comportements inattendus nécessitant un débogage.

0
Peter
'use strict';
const Hapi = require('hapi');
const Basic = require('hapi-auth-basic');
const server = new Hapi.Server();
server.connection({
    port: 2090,
    Host: 'localhost'
});


var vorpal = require('vorpal')();
const chalk = vorpal.chalk;
var fs = require("fs");

var utenti = [{
        name: 'a',
        pass: 'b'
    },
    {
        name: 'c',
        pass: 'd'
    }
];

const users = {
    john: {
        username: 'john',
        password: 'secret',
        name: 'John Doe',
        id: '2133d32a'
    },
    paul: {
        username: 'paul',
        password: 'password',
        name: 'Paul Newman',
        id: '2133d32b'
    }
};

var messaggi = [{
        destinazione: 'a',
        sorgente: 'c',
        messsaggio: 'ciao'
    },
    {
        destinazione: 'a',
        sorgente: 'c',
        messsaggio: 'addio'
    },
    {
        destinazione: 'c',
        sorgente: 'a',
        messsaggio: 'arrivederci'
    }
];

var login = '';
var loggato = false;

vorpal
    .command('login <name> <pass>')
    .description('Effettua il login al sistema')
    .action(function (args, callback) {
        loggato = false;
        utenti.forEach(element => {
            if ((element.name == args.name) && (element.pass == args.pass)) {
                loggato = true;
                login = args.name;
                console.log("Accesso effettuato");
            }
        });
        if (!loggato)
            console.log("Login e Password errati");
        callback();
    });

vorpal
    .command('leggi')
    .description('Leggi i messaggi ricevuti')
    .action(function (args, callback) {
        if (loggato) {
            var estratti = messaggi.filter(function (element) {
                return element.destinazione == login;
            });

            estratti.forEach(element => {
                console.log("mittente : " + element.sorgente);
                console.log(chalk.red(element.messsaggio));
            });
        } else {
            console.log("Devi prima loggarti");
        }
        callback();
    });

vorpal
    .command('invia <dest> "<messaggio>"')
    .description('Invia un messaggio ad un altro utente')
    .action(function (args, callback) {
        if (loggato) {
            var trovato = utenti.find(function (element) {
                return element.name == args.dest;
            });
            if (trovato != undefined) {
                messaggi.Push({
                    destinazione: args.dest,
                    sorgente: login,
                    messsaggio: args.messaggio
                });
                console.log(messaggi);
            }
        } else {
            console.log("Devi prima loggarti");
        }
        callback();
    });

vorpal
    .command('crea <login> <pass>')
    .description('Crea un nuovo utente')
    .action(function (args, callback) {
        var trovato = utenti.find(function (element) {
            return element.name == args.login;
        });
        if (trovato == undefined) {
            utenti.Push({
                name: args.login,
                pass: args.pass
            });
            console.log(utenti);
        }
        callback();
    });

vorpal
    .command('file leggi utenti')
    .description('Legge il file utenti')
    .action(function (args, callback) {
        var contents = fs.readFileSync("utenti.json");
        utenti = JSON.parse(contents);
        callback();
    });

vorpal
    .command('file scrivi utenti')
    .description('Scrive il file utenti')
    .action(function (args, callback) {
        var jsontostring = JSON.stringify(utenti);
        fs.writeFile('utenti.json', jsontostring, function (err) {
            if (err) {
                return console.error(err);
            }
        });
        callback();
    });

vorpal
    .command('file leggi messaggi')
    .description('Legge il file messaggi')
    .action(function (args, callback) {
        var contents = fs.readFileSync("messaggi.json");
        messaggi = JSON.parse(contents);
        callback();
    });

vorpal
    .command('file scrivi messaggi')
    .description('Scrive il file messaggi')
    .action(function (args, callback) {
        var jsontostring = JSON.stringify(messaggi);
        fs.writeFile('messaggi.json', jsontostring, function (err) {
            if (err) {
                return console.error(err);
            }
        });
        callback();
    });

// leggi file , scrivi file

vorpal
    .delimiter(chalk.yellow('messaggi$'))
    .show();




const validate = function (request, username, password, callback) {
    loggato = false;


    utenti.forEach(element => {
        if ((element.name == username) && (element.pass == password)) {
            loggato = true;
            console.log("Accesso effettuato");
            return callback(null, true, {
                name: username
            })
        }
    });
    if (!loggato)
        return callback(null, false);
};

server.register(Basic, function (err) {
    if (err) {
        throw err;
    }
});

server.auth.strategy('simple', 'basic', {
    validateFunc: validate
});



server.route({
    method: 'GET',
    path: '/',
    config: {
        auth: 'simple',
        handler: function (request, reply) {
            reply('hello, ' + request.auth.credentials.name);
        }
    }
});

//route scrivere
server.route({
    method: 'POST',
    path: '/invia',
    config: {
        auth: 'simple',
        handler: function (request, reply) {
            //console.log("Received POST from " + request.payload.name + "; id=" + (request.payload.id || 'anon'));
            var payload = encodeURIComponent(request.payload)
            console.log(request.payload);
            console.log(request.payload.dest);
            console.log(request.payload.messaggio);
            messaggi.Push({
                destinazione: request.payload.dest,
                sorgente: request.auth.credentials.name,
                messsaggio: request.payload.messaggio
            });
            var jsontostring = JSON.stringify(messaggi);
            fs.writeFile('messaggi.json', jsontostring, function (err) {
                if (err) {
                    return console.error(err);
                }
            });
            console.log(messaggi);
            reply(messaggi[messaggi.length - 1]);

        }
    }
});


//route leggere (json)
server.route({
    method: 'GET',
    path: '/messaggi',
    config: {
        auth: 'simple',
        handler: function (request, reply) {
            messaggi = fs.readFileSync("messaggi.json");
            var estratti = messaggi.filter(function (element) {
                return element.destinazione == request.auth.credentials.name;
            });
            var s = [];

            console.log(request.auth.credentials.name);
            console.log(estratti.length);
            estratti.forEach(element => {

                s.Push(element);

                //fare l'array con stringify
                //s+="mittente : "+element.sorgente+": "+element.messsaggio+"\n";

            });
            var a = JSON.stringify(s);
            console.log(a);
            console.log(s);
            reply(a);
        }
    }
});



server.start(function () {
    console.log('Hapi is listening to ' + server.info.uri);
});

function EseguiSql(connection, sql, reply) {
    var rows = [];
    request = new Request(sql, function (err, rowCount) {
        if (err) {
            console.log(err);
        } else {
            console.log(rowCount + ' rows');
            console.log("Invio Reply")
            reply(rows);
        }
    });

    request.on('row', function (columns) {
        var row = {};
        columns.forEach(function (column) {
            row[column.metadata.colName] = column.value;
        });
        rows.Push(row);
    });

    connection.execSql(request);
}

server.route({
    method: 'POST',
    path: '/query',
    handler: function (request, reply) {
        // Qui dovrebbe cercare i dati nel body e rispondere con la query eseguita
        var connection = new Connection(config);

        // Attempt to connect and execute queries if connection goes through
        connection.on('connect', function (err) {
            if (err) {
                console.log(err);
            } else {

                console.log('Connected');
                console.log(request.payload.sql);
                EseguiSql(connection, request.payload.sql, reply);
            }
        });

    }
});

server.connection({
    Host: process.env.Host || 'localhost',
    port: process.env.PORT || 8080
});

var config = {
    userName: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    server: process.env.DB_SERVER,
    options: {
        database: process.env.DB_NAME,
        encrypt: true
    }
}
0
alberdo dellagiacoma

Juste un autre point à ajouter, Hapi a commencé à prendre en charge les appels «http2» à partir de la version 16 (si je ne me trompe pas). Cependant, express n’a pas encore pris en charge le module 'http2' jusqu’à express 4. Bien qu’ils aient publié la fonctionnalité dans la version alpha d’express 5.

0
UchihaItachi
    const Hapi = require('hapi');
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
const server = new Hapi.Server();
var vorpal = require('vorpal')();

server.connection({
    Host: process.env.Host || 'localhost',
    port: process.env.PORT || 3000
});
server.start(function (err) {
    if (err) {
        throw err;
    }
    console.log("server running at : " + server.info.uri);
});

var config =
{
    userName: 'sa',
    password: 'password.123',
    server: 'localhost',

    options:
    {
        database: '',
        port: 1433
    }
}

server.route(
    {
        method: 'GET',
        path: '/{categoria}',
        handler: function (request, reply) {
            var connection = new Connection(config);
            connection.on('connect', function (err) {
                if (err) {
                    console.log(err);
                }
                else {
                    console.log('Connected');
                    EseguiSqlGet(connection, request.params.categoria, reply);
                }
            });
        }
    }
);
function EseguiSqlGet(connection, cat, reply) {
    var rows = [];
    var sql = 'SELECT * FROM Prodotti INNER JOIN Categorie
 on Categorie.IdCategoria = Prodotti.IdCategoria
 WHERE Categorie.IdCategoria = ' + cat ;
    request_sql = new Request(sql, function(err, rowCount) {
        if (err) {
            console.log(err);
        } else {
            console.log(rowCount + ' rows');
            console.log("Invio Reply")
            reply(rows);
        }
    });

    request_sql.on('row', function(columns) {
        var row = {};
        columns.forEach(function (column) {
            row[column.metadata.colName] = column.value;
        });
        rows.Push(row);
    });

    connection.execSql(request_sql);
}
// POST
server.route(
    {
        method: 'POST',
        path: '/inserisci',
        handler: function (request, reply) {
            var connection = new Connection(config);
            connection.on('connect', function (err) {
                if (err) {
                    console.log(err);
                }
                else {
                    console.log('Connected');
                    EseguiSqlPost(connection,reply, 
request.payload.idcat, request.payload.nome, request.payload.prezzo );
                }
            });
        }
    }
);
function EseguiSqlPost(connection,reply, cat,nome,prezzo) {

    var sql = "INSERT INTO Prodotti
 VALUES("+ cat +",'"+nome+"',"+prezzo+")";
    request_sql = new Request(sql, function(err, rowCount) {
        if (err) {
            console.log(err);
        } else {
            console.log(rowCount + ' rows');
            console.log("Invio Reply")
            reply('riga aggiunta');
        }
    });

    /*request_sql.on('row', function(columns) {
        var row = {};
        columns.forEach(function (column) {
            row[column.metadata.colName] = column.value;
        });
        rows.Push(row);
    });
*/
    connection.execSql(request_sql);
}






//VORPAL COMMAND PROMT
var categoria = [
    {

        'idcategoria':'1',
        'nome':'ciao',

    }
]


vorpal
    .command('inserisci <categoria> <nome>')
    .action(function(args, callback)
    {
        categoria.Push(   
{'idcategoria':args.categoria,'nome':args.nome}     );
        console.log(JSON.stringify(categoria));
        callback();
    });

vorpal
.delimiter("delimeter")
.show();
0