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?
Mon organisation part avec Hapi. C'est pourquoi nous l'aimons.
Hapi c'est:
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:
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.
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.
Tout ce que vous pouvez réaliser avec express peut également être facilement réalisé avec hapi.js.
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.
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
Si vous codez dans hapi.js, un code serait très maintenable.
J'ai commencé à utiliser Hapi récemment et j'en suis assez content. Mes raisons sont
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/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.Cela fonctionne hors de la boîte par exemple. fichiers téléchargés , renvoient des flux à partir de points de terminaison, etc.
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.
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.
'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
}
}
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.
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();