Express와 hapi는 서로 어떻게 비교됩니까?
웹 애플리케이션 설계 및 개발 관점에서 Express와 Hapi는 서로 어떻게 비교됩니까?기본적인 예의 경우는 비슷해 보이지만 전반적인 애플리케이션 구조의 주요 차이점에 대해 자세히 알고 싶습니다.
예를 들어, 제가 배운 한, Hapi는 등록 순서를 고려하지 않고, 더 빠른 조회를 할 수 있지만 Express에 비해 제한적인 다른 라우팅 메커니즘을 사용합니다.다른 중요한 차이점이 있습니까?
새로운 npmjs.com 웹사이트를 개발하기 위해 Hapi를 선택했다는 기사도 있습니다. 이 기사는 "Hapi의 플러그인 시스템은 향후 마이크로 서비스를 허용하는 방식으로 애플리케이션의 다양한 측면과 서비스를 분리할 수 있음을 의미합니다.반면 Express는 동일한 기능을 얻기 위해서는 구성이 조금 더 필요합니다.", 정확히 무엇을 의미합니까?
이 문제는 매우 큰 문제이며 긴 답변을 완료해야 하므로 가장 중요한 차이점 중 일부만 다루도록 하겠습니다.아직 답변이 긴 점 사과드립니다.
그것들은 어떻게 비슷합니까?
당신의 말이 전적으로 옳습니다.
기본적인 예를 들어보면 유사해 보입니다.
두 프레임워크 모두 동일한 기본 문제를 해결하고 있습니다.노드에 HTTP 서버를 구축하기 위한 편리한 API 제공즉, 하위 레벨의 네이티브 모듈을 단독으로 사용하는 것보다 더 편리합니다.http
모듈은 우리가 원하는 모든 것을 할 수 있지만 애플리케이션을 작성하는 것은 지루합니다.
이를 위해 라우팅, 핸들러, 플러그인, 인증 모듈과 같은 상위 수준의 웹 프레임워크에 오랫동안 존재해 온 개념을 둘 다 사용합니다.그들이 항상 같은 이름을 가지고 있지는 않았을 수도 있지만 거의 동등합니다.
대부분의 기본적인 예는 다음과 같습니다.
- 경로생성
- 경로 요청 시 함수 실행, 응답 준비
- 요청에 응답합니다.
익스프레스:
app.get('/', function (req, res) {
getSomeValue(function (obj) {
res.json({an: 'object'});
});
});
hapi:
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
getSomeValue(function (obj) {
reply(obj);
});
}
});
여기서 차이가 획기적인 것은 아니죠?그럼 왜 다른 것보다 하나를 선택하는 거지?
그들은 어떻게 다릅니까?
간단한 대답은 hapi가 훨씬 더 많고 즉시 사용할 수 있다는 것입니다.위에서 간단한 예만 봐도 명확하지 않을 수 있습니다.사실 이것은 의도적인 것입니다.간단한 경우는 단순하게 유지됩니다.그럼 몇 가지 큰 차이점을 살펴보도록 하겠습니다.
철학
Express는 매우 최소화됩니다. Δ Δ Δ API Δ ΔΔ Δ 위에 얇은 작은 합니다.http
, 추가 기능을 추가하는 것에 관해서는 여전히 독자적입니다.수신 요청의 본문을 읽으려면(상당히 일반적인 작업) 별도의 모듈을 설치해야 합니다.해당 경로로 다양한 컨텐츠 유형이 전송될 것으로 예상되는 경우에는 다음을 확인해야 합니다.Content-type
헤더는 어떤 것인지 확인하고 그에 따라 파싱합니다(예를 들어 폼 데이터 대 JSON 대 멀티 파트). 종종 별도의 모듈을 사용합니다.
hapi에는 코드를 작성해야 하는 것이 아니라 종종 구성 옵션을 통해 노출되는 풍부한 기능 집합이 있습니다.예를 들어, 처리기를 실행하기 전에 요청 본체(payload)를 메모리에 완전히 읽어와 적절히(콘텐츠 유형에 따라 자동으로) 구문 분석하려면 간단한 옵션에 불과합니다.
server.route({
config: {
payload: {
output: 'data',
parse: true
}
},
method: 'GET',
path: '/',
handler: function (request, reply) {
reply(request.payload);
}
});
특징들
두 프로젝트의 API 설명서만 비교하면 hapi가 더 큰 피쳐 세트를 제공합니다.
hapi에는 Express에서 제공하지 않는 다음 기능이 내장되어 있습니다(제가 아는 한).
- 입력 및 응답 확인(Joi를 통해)
- 몇 줄의 구성으로 사용할 수 있는 여러 스토리지 옵션(mongo, S3, redis, riak)을 갖춘 서버측 캐싱
- 쿠키파싱
- 세션
- 파일 업로드/다부분 구문 분석
- CORS 지지대
- 로깅
확장성 및 모듈화
hapi와 Express는 상당히 다른 방식으로 확장성을 추구합니다.Express를 사용하면 미들웨어 기능이 있습니다.미들웨어 기능은 당신이 쌓아두는 일종의 필터와 같은 것으로, 핸들러를 치기 전에 모든 요청이 그것들을 통해 실행됩니다.
hapi는 요청 라이프사이클을 가지며 미들웨어 기능과 유사하지만 요청 라이프사이클에 여러 정의된 포인트가 존재하는 확장 포인트를 제공합니다.
월마트가 hapi를 구축하고 Express를 사용하지 않게 된 이유 중 하나는 Express 앱을 별도의 부품으로 분리하고 다른 팀원들이 자신의 청크에서 안전하게 작업하도록 하는 것이 얼마나 어려운지에 대한 좌절감이었습니다.이러한 이유로 그들은 hapi로 플러그인 시스템을 만들었습니다.
플러그인은 하위 어플리케이션과 같은 것으로, hapi 앱에서 할 수 있는 모든 것을 할 수 있고 경로, 확장 지점 등을 추가할 수 있습니다.플러그인에서는 경로에 대한 등록 순서가 중요하지 않고 충돌하는 경로를 만들 수 없기 때문에 응용 프로그램의 다른 부분을 위반하지 않을 수 있습니다.그런 다음 이 플러그인을 서버에 결합하여 배포할 수 있습니다.
생태계
Express는 기본 제공량이 거의 없기 때문에 프로젝트에 추가해야 할 때는 외부를 확인해야 합니다.hapi와 함께 작업할 때 필요한 기능이 내장되어 있거나 코어 팀에서 만든 모듈이 있는 경우가 많습니다.
미니멀한 것이 좋습니다.하지만 진지한 제작 앱을 만들고 있다면 결국 이 모든 것들이 필요하게 될 가능성이 높습니다.
보안.
hapi는 Walmart의 팀이 블랙 프라이데이 트래픽을 실행하기 위해 설계한 것이므로 보안과 안정성이 항상 최우선 관심사였습니다.이러한 이유로 이 프레임워크는 프로세스 메모리 소진을 방지하기 위해 수신 페이로드 크기를 제한하는 등 추가적인 작업을 많이 수행합니다.또한 최대 이벤트 루프 지연, 사용된 최대 RSS 메모리, v8 힙의 최대 크기와 같은 옵션이 있으며, 이 옵션을 초과하면 서버가 크래시만 하는 것이 아니라 503 타임아웃으로 응답합니다.
요약
두 사람 모두 직접 평가해 보세요.고객의 요구 사항과 가장 큰 관심사를 해결하는 두 가지 중 어떤 것이 있는지 생각해 보십시오.두 커뮤니티(IRC, Gitter, Github)에 참여해보고, 어떤 것을 선호하는지 확인해보세요.내 말만 믿지 마.행복한 해킹!
면책 사항: 저는 hapi에 관한 책의 저자로서 편견을 가지고 있으며, 위의 내용은 대체로 제 개인적인 의견입니다.
우리 조직은 Hapi와 함께 합니다.이것이 우리가 좋아하는 이유입니다.
하피(Hapi):
- 주요 군단의 지원을 받습니다.이는 커뮤니티 지원이 강력할 것이며, 향후 출시 기간 내내 고객님을 위한 지원이 될 것입니다.열정적인 Hapi 사람들을 쉽게 찾을 수 있고, 좋은 튜토리얼도 있습니다(ExpressJs 튜토리얼만큼 수가 많고 넓지는 않지만).이 게시일 현재 npm 과 월마트는 Hapi를 사용하고 있습니다.
- 나머지 API 표면에 대한 포괄적인 지식 없이도 백엔드 서비스의 여러 부분에 대해 작업하는 분산된 팀의 작업을 용이하게 할 수 있습니다(Hapi의 플러그인 아키텍처는 이러한 품질의 전형입니다).
- 프레임워크가 프레임워크가 해야 할 일을 수행하도록 합니다. 즉, 구성합니다.그 후에는 프레임워크가 보이지 않고 개발자들이 비즈니스 로직을 구축하는 데 실질적인 크리에이티브 에너지를 집중할 수 있도록 해야 합니다.1년 동안 Hapi를 사용해보니 Hapi가 확실히 이를 이루었다고 생각합니다.전... 행복해요!
Eran Hammer(하피의 리드)에게 직접 듣고 싶다면
지난 4년 동안 hapi는 크든 작든 많은 프로젝트에서 선택의 틀이 되었습니다.hapi를 독특하게 만드는 것은 대규모 배포 및 대규모 팀으로 확장할 수 있다는 점입니다.프로젝트가 성장함에 따라 프로젝트의 복잡성, 즉 엔지니어링 복잡성과 프로세스 복잡성도 증가합니다.hapi의 아키텍처와 철학은 코드를 끊임없이 재팩터링할 필요 없이 증가된 복잡성을 처리합니다 [더 읽어보기].
Hapi는 그와 같은 "스타 파워"를 가지고 있지 않기 때문에 ExpressJs처럼 시작하기는 쉽지 않을 것입니다. 하지만 일단 Hapi가 편안해지면 많은 마일리지를 얻을 수 있을 것입니다.몇 년 동안 ExpressJ를 무책임하게 사용한 새로운 해커로서 약 2개월이 걸렸습니다.경험이 풍부한 백엔드 개발자라면 문서를 읽는 방법을 알고 있을 것이고, 아마도 이 사실조차 눈치채지 못할 것입니다.
Hapi 설명서가 개선할 수 있는 분야는 다음과 같습니다.
- 사용자 인증 및 세션 생성 방법
- 교차 원점 요청(CORS) 처리
- 파일 업로드(파일 업로드, smart, chunked)
인증은 어떤 종류의 인증 전략(Basic Authentication, Cookies, JWT Tokens, OAuth)을 사용할지 결정해야 하기 때문에 가장 어려운 부분이 될 것 같습니다.Hapi의 문제가 아니라도 세션/인증 환경이 이렇게 단편적으로...하지만 그들이 이 일을 위해 도움을 줬으면 합니다.개발자의 행복감을 크게 높여줄 것입니다.
나머지 두 가지는 사실 그렇게 어렵지는 않아요, 문서가 조금 더 잘 작성될 수도 있어요.
Hapi에 관한 빠른 사실들 또는 왜 Hapi JS인가요?
Hapi는 구성 중심입니다. 프레임워크에 인증 및 권한이 내장되어 있습니다. 전투 테스트를 거친 분위기에서 출시되었으며 그 가치가 정말로 입증되었습니다. 모든 모듈은 100% 테스트 커버리지를 가지고 있습니다. 핵심 HTTP에서 벗어나 가장 높은 수준의 추상화를 등록합니다. 플러그인 아키텍처를 통해 쉽게 공감할 수 있습니다.
Hapi는 더 나은 성능을 제공합니다 Hapi는 다른 라우팅 메커니즘을 사용하므로 더 빠른 조회를 수행하고 등록 순서를 고려할 수 있습니다.그럼에도 불구하고 익스프레스와 비교하면 상당히 제한적입니다.그리고 Hapi 플러그인 시스템 덕분에 앞으로 응용 프로그램에 도움이 될 다양한 측면과 서비스를 분리할 수 있습니다.
사용.
Express와 비교했을 때 가장 선호되는 프레임워크는 Hapi입니다.Hapi는 주로 대규모 엔터프라이즈 애플리케이션에 사용됩니다.
개발자들이 엔터프라이즈 애플리케이션을 만들 때 Express를 선택하지 않는 몇 가지 이유는 다음과 같습니다.
Express에서 경로를 구성하기가 더 어렵습니다.
미들웨어는 대부분의 경우 방해가 됩니다. 경로를 정의할 때마다 코드 수만큼 작성해야 합니다.
RESTful API를 구축하려는 개발자에게는 Hapi가 가장 좋은 선택이 될 것입니다.Hapi는 마이크로 서비스 아키텍처를 가지고 있으며 특정 매개 변수를 기반으로 한 핸들러에서 다른 핸들러로 제어권을 이전할 수도 있습니다.Hapi 플러그인을 사용하면 쉽게 관리할 수 있는 비즈니스 로직을 분할할 수 있기 때문에 HTTP를 중심으로 더 높은 수준의 추상화를 즐길 수 있습니다.
Hapi의 또 다른 큰 장점은 잘못 구성했을 때 자세한 오류 메시지를 제공한다는 것입니다.또한 Hapi를 사용하면 파일 업로드 크기를 기본적으로 구성할 수 있습니다.최대 업로드 크기가 제한된 경우 파일 크기가 너무 크다는 오류 메시지를 사용자에게 전송할 수 있습니다.이렇게 하면 파일 업로드가 더 이상 전체 파일을 버퍼링하지 않으므로 서버가 손상되지 않도록 보호할 수 있습니다.
express를 사용하여 달성할 수 있는 모든 것은 hapi.js를 사용하여 쉽게 달성할 수 있습니다.
Hapi.js는 매우 스타일리시하고 코드를 잘 정리합니다.이것이 어떻게 라우팅을 하고 핵심 논리를 컨트롤러에 넣는지를 본다면 도전적으로 좋아하게 될 것입니다.
Hapi.js는 공식적으로 hapi.js 전용 플러그인을 여러 개 제공합니다. 토큰 기반 인증에서부터 세션 관리 등 다양한 것들이 추가됩니다.기존의 npm을 사용할 수 없다는 의미는 아니고 모두 hapi.js에서 지원합니다.
hapi.js로 코드화하면 코드를 유지할 수 있습니다.
저는 최근에 Hapi를 사용하기 시작했는데 꽤 마음에 듭니다.제 이유는
테스트하기 쉽습니다.예를 들어,
server.inject
앱을 실행하고 듣지 않아도 응답을 얻을 수 있습니다.server.info
현재 uri, port 등을 제공합니다.server.settings
구성에 액세스합니다.server.settings.cache
현재 캐시 공급자를 가져옵니다.- 의심스러울 때는
/test
앱의 모든 부분에 대한 폴더 또는 지원되는 플러그인을 통해 모의/테스트/제거 방법 등에 대한 제안을 확인할 수 있습니다. - 내 감각은 hapi의 건축 모델이 당신이 신뢰할 수 있게 해주지만 예를 들어 검증할 수 있게 해준다는 것입니다.플러그인이 등록되어 있습니까?모듈 종속성을 선언하려면 어떻게 해야 합니까?
핵심 라이브러리와 함께 필수 플러그인(예: 템플릿 파싱, 캐싱 등)을 유지 관리합니다.부가적인 이점은 필수적인 것들에 걸쳐 동일한 코딩 표준이 적용된다는 것입니다.
정상적인 오류 및 오류 처리.Hapi는 구성 옵션을 검증하고 내부 경로 테이블을 유지하여 중복 경로를 방지합니다.디버깅이 필요한 예상치 못한 동작 대신 오류를 조기에 처리하기 때문에 학습 중에 상당히 유용합니다.
Hapi는 버전 16 이후부터 'http2' 호출을 지원하기 시작했습니다(제가 틀리지 않은 경우).그러나 express는 express 4까지 'http2' 모듈을 직접 지원하지 않습니다.비록 그들은 익스프레스 5의 알파 버전으로 그 기능을 출시했습니다.
'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
}
}
언급URL : https://stackoverflow.com/questions/30469767/how-do-express-and-hapi-compare-to-each-other
'programing' 카테고리의 다른 글
입력 유형 = 파일 표시 전용 버튼 (0) | 2023.09.06 |
---|---|
mingw로 mariadb C 커넥터를 만들 수 없습니다. (0) | 2023.09.06 |
java.sql.SQL 비일시적 연결Maria에게 데이터 프레임을 쓰는 동안 예외가 발생했습니다.DB (0) | 2023.09.06 |
PHPMyAdmin 데이터베이스 연결 오류 (0) | 2023.09.06 |
EditText 깜박이는 커서 사용 안 함 (0) | 2023.09.06 |