node.js hapi
At the core of the JavaScript language is its asynchronous programming model. Unfortunately, dealing with callback functions has long been a source of frustration for many developers. JavaScript Promises helped make writing complex asynchronous code more manageable, but brought its own set of challenges. With the introduction of async functions in ES2017 (and the async
and await
keywords), writing asynchronous JavaScript is now much easier.
JavaScript语言的核心是其异步编程模型。 不幸的是,处理回调函数长期以来一直使许多开发人员感到沮丧。 JavaScript Promises帮助使编写复杂的异步代码更易于管理,但也带来了一系列挑战。 随着ES2017中引入异步函数(以及async
和await
关键字),编写异步JavaScript现在变得更加容易。
Hapi is one of many frameworks available for Node.js designed for building scalable web application and services. With the release of version 17, hapi has been completely overhauled to use JavaScript async functions. The result is a modern framework for Node.js that is a pleasure to use.
Hapi是可用于Node.js的众多框架之一,这些框架旨在构建可伸缩的Web应用程序和服务。 随着版本17的发布,已经彻底修改了hapi以使用JavaScript异步功能。 结果是使用Node.js的一种现代框架。
Most Node.js tutorials available today were written for older versions of Node.js using older ES5 syntax. In this tutorial, you will learn the basics of creating a Node.js web application with hapi using more modern JavaScript.
今天可用的大多数Node.js教程都是使用较旧的ES5语法为较旧版本的Node.js编写的。 在本教程中,您将学习使用更现代JavaScript使用hapi创建Node.js Web应用程序的基础知识。
Open up a terminal (Mac/Linux) or a command prompt (Windows) and type the following command:
打开终端(Mac / Linux)或命令提示符(Windows)并键入以下命令:
node --version
If you get an error, or the version of Node.js you have is less than version 8, you'll need to install Node.js. On Mac or Linux, I recommend you first install nvm and use nvm to install Node.js. On Windows, I recommend you use Chocolatey.
如果出现错误,或者您拥有的Node.js版本低于版本8,则需要安装Node.js。 在Mac或Linux上,建议您首先安装nvm,然后使用nvm安装Node.js。 在Windows上,建议您使用Chocolatey 。
After ensuring you have a recent version of Node.js installed, create a folder for your project.
确保安装了最新版本的Node.js之后,为您的项目创建一个文件夹。
mkdir learning-hapi
cd learning-hapi
A package.json
file is required for your Node.js project and includes things like project information, scripts, and project dependencies. Use the npm
command to create a package.json
file in the project folder.
您的Node.js项目需要package.json
文件,其中包括项目信息,脚本和项目依赖项。 使用npm
命令在项目文件夹中创建package.json
文件。
npm init -y
Next, install hapi as a dependency.
接下来,安装hapi作为依赖项。
npm install hapi
Now open the project in your editor of choice.
现在,在您选择的编辑器中打开项目。
If you don't already have a favorite code editor, I recommend installing Visual Studio Code. VS Code has exceptional support for JavaScript and Node.js, such as smart code completion and debugging. There's also a vast library of free extensions contributed by the community.
如果您还没有喜欢的代码编辑器,建议安装Visual Studio Code 。 VS Code对JavaScript和Node.js具有出色的支持,例如智能代码完成和调试。 还有一个由社区提供的庞大的免费扩展库。
Next, create a folder named src
. In this folder, add a new file named index.js
. Open the file and add the following JavaScript.
接下来,创建一个名为src
的文件夹。 在此文件夹中,添加一个名为index.js
的新文件。 打开文件并添加以下JavaScript。
"use strict";
const Hapi = require( "hapi" );
const port = 8080;
const server = Hapi.server( { port } );
// Define a route for the URL http://localhost:8080/
server.route( {
method: "GET",
path: "/",
handler: () => {
// a handler can return text, HTML, JSON,
// a file, or just about anything.
return "My first hapi server!";
}
} );
const start = async () => {
try {
// start the server
await server.start();
console.log( `Server running at http://localhost:${ port }` );
} catch ( err ) {
console.log( err );
process.exit( 1 );
}
};
start();
As you can see in the previous code, the line const start = async () => {
declares an asynchronous function named start
using the arrow function syntax. server.start()
is itself an asynchronous function, which requires the await
keyword. The await
keyword instructs the application to pause execution until the async function completes before moving on to the next line of code.
如您在前面的代码中所见, const start = async () => {
使用箭头函数语法声明了一个名为start
的异步函数。 server.start()
本身是一个异步函数,需要await
关键字。 关键字await
指示应用程序暂停执行,直到异步功能完成为止,然后再继续执行下一行代码。
Dealing with errors in asynchronous code before async/await
was tricky at best. Another advantage of using async
/await
is the ability to use straight-forward try
/catch
blocks to catch any errors that may occur.
在async/await
之前处理异步代码中的错误充其量是棘手的。 使用async
/ await
另一个优点是可以使用直接try
/ catch
块来捕获可能发生的任何错误。
Next, edit the package.json
file and change the "main"
property value to "src/index.js"
. This property points Node to a file to execute when the application starts.
接下来,编辑package.json
文件,并将"main"
属性值更改为"src/index.js"
。 该属性将Node指向要在应用程序启动时执行的文件。
"main": "src/index.js",
Now you can start the application. Go back to the terminal window and type in the following command.
现在您可以启动应用程序。 返回终端窗口并输入以下命令。
node.
You should see the message Server running at http://localhost:8080
. Open your browser and navigate to http://localhost:8080
. Your browser should display something like the following.
您应该看到消息Server running at http://localhost:8080
。 打开浏览器并导航到http://localhost:8080
。 您的浏览器应显示类似以下内容。
As a Node.js project grows beyond a simple "hello world" example, it's essential to set up a good project structure. There are countless opinions on how you might organize a project, but a good starting point for a web application might look something like the following.
随着Node.js项目的发展超越了一个简单的“ hello world”示例,建立一个良好的项目结构至关重要。 关于如何组织项目的观点不计其数,但是Web应用程序的一个好的起点可能类似于以下内容。
├── package.json
├── client
│ ├── index.html
│ ├── css
│ └── js
├── src
│ ├── app.js
│ ├── index.js
│ ├── plugins
│ │ └── index.js
│ ├── routes
│ │ └── index.js
│ └── views
└── test
└── index.js
Hapi can serve static files, such as HTML, CSS, and front-end JavaScript, using the inert plugin (more on plugins later). The client
folder is where you might store these static assets. Of course, the contents and structure inside the client
folder may differ depending on your front-end framework of choice.
Hapi可以使用inert插件(稍后将在更多插件中介绍)提供静态文件,例如HTML,CSS和前端JavaScript。 您可以在client
文件夹中存储这些静态资产。 当然, client
文件夹中的内容和结构可能会有所不同,具体取决于您选择的前端框架。
Under the src
folder, you might organize your server-side code into the following files and folders:
在src
文件夹下,您可以将服务器端代码组织到以下文件和文件夹中:
app.js
to configure the hapi server, app.js
来配置hapi服务器, index.js
to start the server, index.js
启动服务器, plugins
for registering external and custom hapi plugins, plugins
用于注册外部和定制HAPI插件, routes
for defining the resources, or URIs, of your application, 用于定义应用程序资源或URI的 routes
, views
for any back-end dynamically-rendered content. 以及任何后端动态呈现内容的views
。 Hapi can render server-side content using the vision plugin combined with a template engine such as ejs, handlebars, or pug. It is up to you whether you want your application to serve static content using inert
, server-rendered content using vision
, or a mixture of both.
哈皮可以使使用服务器侧内容视力插件与模板发动机组合如EJS , 车把 ,或哈巴狗 。 由您决定是否要让应用程序使用inert
内容提供静态内容,使用vision
服务器渲染内容或同时使用两者。
Note: If you are building an application that only acts as a service or only exposes an API, you may not have a need for the inert and vision plugins, or a need to have folders for client and views.
注意:如果要构建仅充当服务或仅公开API的应用程序,则可能不需要惰性和视觉插件,也不需要具有用于客户端和视图的文件夹。
Before continuing further, refactor your project with the following steps.
在继续之前,请按照以下步骤重构项目。
Create a folder under src
named plugins
. Create a new file under src/plugins
named index.js
. Add the following code to this file.
在src
下创建一个名为plugins
的文件夹。 在src/plugins
下创建一个名为index.js
的新文件。 将以下代码添加到该文件。
"use strict";
module.exports.register = async server => {
// more to come later
};
Create a new folder under src
named routes
. Create a new file under src/routes
named index.js
. Add the following code to this file.
在src
下创建名为routes
的新文件夹。 在src/routes
下创建一个名为index.js
的新文件。 将以下代码添加到该文件。
"use strict";
module.exports.register = async server => {
server.route( {
method: "GET",
path: "/",
handler: () => {
// a handler can return text, HTML, JSON,
// a file, or just about anything
return "My first hapi server!";
}
} );
};
Create a new file under src
named app.js
. Add the following code to this file.
在src
下创建一个名为app.js
的新文件。 将以下代码添加到该文件。
"use strict";
const Hapi = require( "hapi" );
const plugins = require( "./plugins" );
const routes = require( "./routes" );
module.exports.createServer = async config => {
const server = Hapi.server( config );
// register plugins
await plugins.register( server );
// register routes
await routes.register( server );
return server;
};
Last, modify src/index.js
to match the following code.
最后,修改src/index.js
以匹配以下代码。
"use strict";
const app = require( "./app" );
const port = 8080;
const config = { port };
const start = async () => {
try {
// create the server
const server = await app.createServer( config );
// start the server
await server.start();
console.log( `Server running at http://localhost:${ port }` );
} catch ( err ) {
console.log( err );
process.exit( 1 );
}
}
start();
By design, the core hapi service focuses on basic server functionality. Plugins add additional features and capabilities to hapi. Your application may use a mix of official plugins, third-party plugins, and custom plugins you write. Here are just a sample of the more commonly used plugins.
通过设计,核心hapi服务专注于基本服务器功能。 插件为hapi添加了其他功能。 您的应用程序可能混合使用官方插件,第三方插件和您编写的自定义插件。 这只是更常用插件的一个示例。
Plugin | Description |
---|---|
inert | Use to serve static files and directories. |
vision | Render templates. |
blipp | Displays all the defined routes on startup. |
hapi-pino | Fast application logger that logs information in JSON format. |
bell | Third-party authentication. |
插入 | 描述 |
---|---|
惰性的 | 用于提供静态文件和目录。 |
视力 | 渲染模板。 |
Blipp | 在启动时显示所有定义的路由。 |
匹皮诺 | 快速的应用程序记录器,以JSON格式记录信息。 |
钟 | 第三方认证。 |
Here are a few useful libraries commonly found in hapi projects.
以下是在hapi项目中常见的一些有用的库。
Library | Description |
---|---|
joi | JSON object schema validation. |
boom | Use to generate and return HTTP error messages. |
bounce | Selectively catch and rethrow errors. |
wreck | Collection of HTTP client utilities. |
lab | Testing framework with code coverage analysis. |
code | Test assertion library to use with lab . |
图书馆 | 描述 |
---|---|
i | JSON对象架构验证。 |
繁荣 | 用于生成和返回HTTP错误消息。 |
弹跳 | 有选择地捕获并重新抛出错误。 |
破坏 | HTTP客户端实用程序的集合。 |
实验室 | 具有代码覆盖率分析的测试框架。 |
码 | 测试断言库以与lab 一起使用。 |
In this next step, install two hapi plugins and configure them. From the command line, install blipp
and hapi-pino
.
在下一步中,安装两个hapi插件并进行配置。 在命令行中,安装blipp
和hapi-pino
。
npm install blipp hapi-pino
Next, modify src/plugins/index.js
and replace the contents of this file with the following code.
接下来,修改src/plugins/index.js
并将该文件的内容替换为以下代码。
"use strict";
const blipp = require( "blipp" );
const pino = require( "hapi-pino" );
const isDev = process.env.NODE_ENV !== "production";
module.exports.register = async server => {
await server.register( [ blipp, {
plugin: pino,
options: {
prettyPrint: isDev,
logEvents: [ "response", "onPostStart" ]
}
} ] );
};
server.register()
can take a single plugin or an array of plugins. A plugin can be registered using an instance of the plugin itself, such as the case with registering blipp
. A plugin can also be registered using the plugin configuration object syntax, demonstrated by registering hapi-pino
with its configuration options.
server.register()
可以使用单个插件或插件数组。 可以使用插件本身的实例来注册插件,例如注册blipp
的情况。 也可以使用插件配置对象语法来注册插件,这通过使用其配置选项注册hapi-pino
演示。
You can create your custom plugins for hapi to do all sorts of things, such as modify server configuration, add routes, or listen for server events. In this step, create a plugin that listens for when a server starts and logs a message.
您可以为hapi创建自定义插件,以执行各种操作,例如修改服务器配置,添加路由或监听服务器事件 。 在此步骤中,创建一个侦听服务器启动并记录消息的插件。
Create a new file under src/plugins
named serverStart.js
. In this file, add the following code.
在src/plugins
下创建一个名为serverStart.js
的新文件。 在此文件中,添加以下代码。
"use strict";
module.exports = {
name: "serverStart",
version: "1.0.0",
register: async ( server, { message } ) => {
server.events.on( "start", () => {
const msg = message || `Server running at ${ server.info.uri }`;
server.log( [ "info", "server" ], msg );
} );
}
};
A hapi plugin is a JavaScript object with a name
property, a version
property, and a register
function with two arguments: server
and options
. Your serverStart
plugin has an asynchronous register
function that takes a server
argument and uses object destructuring to take a message
passed in the options. If the code does not specify a message, the plugin generates a default message.
hapi插件是一个JavaScript对象,具有name
属性, version
属性和具有两个参数的register
函数: server
和options
。 您的serverStart
插件具有异步register
函数,该函数带有server
参数,并使用对象 serverStart
来获取在选项中传递的message
。 如果代码未指定消息,则插件会生成默认消息。
To use this new plugin, modify src/plugins/index.js
with the following code.
要使用此新插件,请使用以下代码修改src/plugins/index.js
。
"use strict";
const Blipp = require( "blipp" );
const HapiPino = require( "hapi-pino" );
const serverStart = require( "./serverStart" );
const isDev = process.env.NODE_ENV !== "production";
module.exports.register = async server => {
await server.register( [ Blipp, {
plugin: HapiPino,
options: {
prettyPrint: isDev,
logEvents: [ "response" ]
}
}, {
plugin: serverStart,
options: {
message: `My hapi server is running at ${ server.info.uri }`
}
} ] );
};
Now run your application using:
现在使用以下命令运行您的应用程序:
node.
Your console output should look similar to the following.
您的控制台输出应类似于以下内容。
method path description
------ ---------------------------- -----------
GET /[1544478627595] INFO (7408 on mycomputer):
tags: [
"info",
"server"
]
data: "My hapi server is running at http://mycomputer:8080"
So far the application has only returned plain text. A hapi application can respond to requests with text, static files, content dynamically generated from templates, or other types of media, such as JSON. In this step, add support for Embedded JavaScript templates, or EJS.
到目前为止,该应用程序仅返回纯文本。 hapi应用程序可以使用文本,静态文件,从模板动态生成的内容或其他类型的媒体(例如JSON)来响应请求。 在此步骤中,添加对嵌入式JavaScript模板或EJS的支持。
First, install the required dependencies using npm
.
首先,使用npm
安装所需的依赖项。
npm install vision ejs
Create a new file in the src/views
folder named layout.ejs
. Add the following HTML to this file.
在src/views
文件夹中创建一个名为layout.ejs
的新文件。 将以下HTML添加到此文件。
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><%= title %>title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
head>
<body>
<%- content %>
body>
html>
Create a new file in the src/views
folder named index.ejs
. Add the following HTML to this file.
在src/views
文件夹中创建一个名为index.ejs
的新文件。 将以下HTML添加到此文件。
<div class="container">
<h1 class="header"><%= title %>h1>
<p><%= message %>p>
div>
Next, update src/app.js
to configure the vision
plugin and ejs
template engine.
接下来,更新src/app.js
以配置vision
插件和ejs
模板引擎。
"use strict";
const Hapi = require( "hapi" );
const vision = require( "vision" );
const ejs = require( "ejs" );
const plugins = require( "./plugins" );
const routes = require( "./routes" );
module.exports.createServer = async config => {
const server = Hapi.server( config );
// add the vision plugin and
// register EJS template view support
await server.register( vision );
server.views( {
engines: { ejs },
relativeTo: __dirname,
path: "views",
layout: true
} );
// register plugins
await plugins.register( server );
// register routes
await routes.register( server );
return server;
};
Now, update src/routes/index.js
to return the rendered view instead of text.
现在,更新src/routes/index.js
以返回渲染的视图而不是文本。
"use strict";
module.exports.register = async server => {
server.route( {
method: "GET",
path: "/",
handler: async ( request, h ) => {
try {
const message = "My first hapi server!";
return h.view( "index", {
title: "Home",
message
} );
} catch ( err ) {
server.log( [ "error", "home" ], err );
}
}
} );
};
Note: In the previous handler function, the request contains information about the incoming request, and h is the response toolkit that includes properties and utilities for creating responses.
注意:在上一个处理程序函数中, 请求包含有关传入请求的信息,h是响应工具包 ,其中包括用于创建响应的属性和实用程序。
Unless you are building a static website, chances are your application needs a way to allow visitors to register for an account, log in, reset their password, and so forth. Add to the mix the ever-changing landscape of security concerns, and you can easily see implementing and maintaining a sound security strategy is far from a trivial task.
除非您建立一个静态网站,否则您的应用程序很可能需要一种允许访问者注册帐户,登录,重设密码等的方法。 加上不断变化的安全问题,您可以轻松地看到实施和维护合理的安全策略绝非易事。
The good news is Okta's developer platform, built on the latest open security standards, makes this step very easy.
好消息是Okta的开发人员平台建立在最新的开放安全性标准之上,使这一步骤非常容易。
To begin, create a free developer account with Okta at developer.okta.com. Click the Create Free Account button, or click the Sign Up button.
首先,请在developer.okta.com上使用Okta创建一个免费的开发人员帐户。 单击创建免费帐户按钮,或单击注册按钮。
After creating your account, click the Applications link at the top, and then click Add Application.
创建帐户后,单击顶部的“ 应用程序”链接,然后单击“ 添加应用程序” 。
Next, choose a Web Application and click Next.
接下来,选择一个Web应用程序 ,然后单击Next 。
Enter a name for your application, such as My Hapi Server. Verify the port number is the same as configured for your local web application. Then, click Done to finish creating the application.
输入您的应用程序的名称,例如My Hapi Server 。 验证端口号与为本地Web应用程序配置的端口号相同。 然后,单击“完成”以完成应用程序的创建。
One of the great features of Okta is allowing users of your application to sign up for an account. By default, this feature is disabled, but you can easily enable it. First, click on the Users menu and select Registration.
Okta的一大功能是允许您的应用程序用户注册一个帐户。 默认情况下,此功能是禁用的,但是您可以轻松地启用它。 首先,单击“ 用户”菜单,然后选择“ 注册” 。
Node.js applications typically use environment variables for configuration. However, managing environment variables can be a chore. A popular module for managing application configuration data is dotenv.
Node.js应用程序通常使用环境变量进行配置。 但是,管理环境变量可能很麻烦。 dotenv是用于管理应用程序配置数据的流行模块。
Install dotenv
as a project dependency.
安装dotenv
作为项目依赖项。
npm install dotenv
Create a file named .env
in the root folder of the project, and add the following configuration.
在项目的根文件夹中创建一个名为.env
的文件,并添加以下配置。
Note: When using a source control system such as git, do not add the .env file to source control. Each environment requires a custom .env file. It is recommended you document the values expected in the .env file in the project README or a separate .env.sample file.
注意:使用git等源代码管理系统时,请勿将.env文件添加到源代码管理中。 每个环境都需要一个自定义的.env文件。 建议您在项目README的.env文件或单独的.env.sample文件中记录所需的值。
# Server configuration
NODE_ENV=production
PORT=8080
HOST_URL=http://localhost:8080
COOKIE_ENCRYPT_PWD=superAwesomePasswordStringThatIsAtLeast32CharactersLong!
# Okta configuration
OKTA_ORG_URL=https://{yourOktaDomain}
OKTA_CLIENT_ID={yourClientId}
OKTA_CLIENT_SECRET={yourClientSecret}
Now, update src/index.js
to use the dotenv
module.
现在,更新src/index.js
以使用dotenv
模块。
"use strict";
// Load in environment configuration
require( "dotenv" ).config();
const app = require( "./app" );
const DEFAULT_PORT = 8080;
const port = process.env.PORT || DEFAULT_PORT;
const config = { port };
const start = async () => {
try {
// create the server
const server = await app.createServer( config );
// start the server
await server.start();
} catch ( err ) {
console.log( err );
process.exit( 1 );
}
};
start();
Go to your Okta account and click on the Dashboard link. On the right side of the page, you should find your Org URL. Copy and paste this value into your .env
file to replace the value for OKTA_ORG_URL
.
转到您的Okta帐户,然后单击“仪表板”链接。 在页面右侧,您应该找到您的组织网址 。 将此值复制并粘贴到您的.env
文件中,以替换OKTA_ORG_URL
的值。
Click on the Applications link, and then click on the name of your new application. Click on the General tab, and find near the bottom of the page a section titled Client Credentials. Copy the Client ID and Client secret values and paste them into your .env
file to replace {yourClientId}
and {yourClientSecret}
, respectively.
单击“ 应用程序”链接,然后单击新应用程序的名称。 点击常规标签,然后在页面底部附近找到标题为客户端凭据的部分。 复制客户ID和客户机密值,然后将其粘贴到您的.env
文件中,分别替换{yourClientId}
和{yourClientSecret}
。
First, install the bell
and hapi-auth-cookie
plugins. Bell is an authentication plugin, and hapi-auth-cookie
is for cookie-based session management.
首先,安装bell
和hapi-auth-cookie
插件。 Bell是身份验证插件,而hapi-auth-cookie
用于基于cookie的会话管理。
npm install bell hapi-auth-cookie
Under src/plugins
create a new file named auth.js
and add the following code.
在src/plugins
创建一个名为auth.js
的新文件,并添加以下代码。
"use strict";
const bell = require( "bell" );
const authCookie = require( "hapi-auth-cookie" );
const isSecure = process.env.NODE_ENV === "production";
module.exports.register = async server => {
// register plugins
await server.register( [ authCookie, bell ] );
// configure cookie authorization strategy
server.auth.strategy( "session", "cookie", {
password: process.env.COOKIE_ENCRYPT_PWD,
redirectTo: "/authorization-code/callback", // If there is no session, redirect here
isSecure // Should be set to true (which is the default) in production
} );
// configure bell to use your Okta authorization server
server.auth.strategy( "okta", "bell", {
provider: "okta",
config: { uri: process.env.OKTA_ORG_URL },
password: process.env.COOKIE_ENCRYPT_PWD,
isSecure,
location: process.env.HOST_URL,
clientId: process.env.OKTA_CLIENT_ID,
clientSecret: process.env.OKTA_CLIENT_SECRET
} );
};
Next, update src/plugins/index.js
to register the new module.
接下来,更新src/plugins/index.js
来注册新模块。
"use strict";
const blipp = require( "blipp" );
const pino = require( "hapi-pino" );
const serverStart = require( "./serverStart" );
const auth = require( "./auth" );
const isDev = process.env.NODE_ENV !== "production";
module.exports.register = async server => {
await server.register( [ blipp, {
plugin: pino,
options: {
prettyPrint: isDev,
logEvents: [ "response" ]
}
}, {
plugin: serverStart,
options: {
message: `My hapi server is running at ${ server.info.uri }`
}
} ] );
await auth.register( server );
};
Now, modify src/routes/index.js
to the following code.
现在,将src/routes/index.js
修改为以下代码。
"use strict";
const boom = require( "boom" );
module.exports.register = async server => {
server.route( {
method: "GET",
path: "/",
config: {
auth: {
strategy: "session",
mode: "optional"
}
},
handler: async ( request, h ) => {
try {
const message = request.auth.isAuthenticated ? `Hello, ${ request.auth.credentials.profile.firstName }!` : "My first hapi server!";
return h.view( "index", {
title: "Home",
message,
isAuthenticated: request.auth.isAuthenticated
} );
} catch ( err ) {
server.log( [ "error", "home" ], err );
}
}
} );
server.route( {
method: "GET",
path: "/login",
options: {
auth: "session",
handler: async request => {
return `Hello, ${ request.auth.credentials.profile.email }!`;
}
}
} );
server.route( {
method: "GET",
path: "/authorization-code/callback",
options: {
auth: "okta",
handler: ( request, h ) => {
if ( !request.auth.isAuthenticated ) {
throw boom.unauthorized( `Authentication failed: ${ request.auth.error.message }` );
}
request.cookieAuth.set( request.auth.credentials );
return h.redirect( "/" );
}
}
} );
server.route( {
method: "GET",
path: "/logout",
options: {
auth: {
strategy: "session",
mode: "try"
},
handler: ( request, h ) => {
try {
if ( request.auth.isAuthenticated ) {
// clear the local session
request.cookieAuth.clear();
}
return h.redirect( "/" );
} catch ( err ) {
request.log( [ "error", "logout" ], err );
}
}
}
} );
};
Create a new folder under src/views
named partials
. Create a new file in the partials
folder named navigation.ejs
. Add the following HTML to this file.
在src/views
下创建一个名为partials
的新文件夹。 在partials
文件夹中创建一个名为navigation.ejs
的新文件。 将以下HTML添加到此文件。
<nav>
<div class="nav-wrapper">
<% if ( isAuthenticated ) { %>
<a href="/logout">Logouta>
<% } else { %>
<a href="/login">Logina>
<% } %>
nav>
Update src/views/layout.ejs
to include the navigation.ejs
file when it renders.
更新src/views/layout.ejs
以使其在呈现时包含navigation.ejs
文件。
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><%= title %>title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
head>
<body>
<% include partials/navigation %>
<%- content %>
body>
html>
Now, you are ready to start your application again.
现在,您可以重新启动应用程序了。
node.
In your browser, navigate to http://localhost:8080/
. Click on the Login button at the top. You should see a prompt to log in to your Okta account.
在浏览器中,导航到http://localhost:8080/
。 单击顶部的“ 登录”按钮。 您应该看到提示登录到您的Okta帐户。
Note: To verify authentication is working as expected, you may need to open a new browser or use a private/incognito browser window.
注意:要验证身份验证是否按预期工作,您可能需要打开新的浏览器或使用专用/隐身浏览器窗口。
After logging in, you should be redirected back to the home page and see something like the following.
登录后,应将您重定向回主页,并看到类似以下的内容。
After authentication, the following profile information is available on every request as part of request.auth
.
认证后,以下配置文件信息可作为request.auth
一部分用于每个请求。
// request.auth (Example)
{
"isAuthenticated": true,
"credentials": {
"provider": "okta",
"token": "...",
"expiresIn": 3600,
"profile": {
"id": "0012345",
"username": "[email protected]",
"firstName": "John",
"lastName": "Henry",
"email": "[email protected]",
"raw": {
"sub": "0012345",
"name": "John Henry",
"locale": "en-US",
"email": "[email protected]",
"preferred_username": "[email protected]",
"given_name": "John",
"family_name": "Henry",
"zoneinfo": "America/Los_Angeles",
"updated_at": 1544212558,
"email_verified": true
}
}
}
}
A hapi project would not be complete without tests. Lab and code are the preferred test libraries for hapi projects. TestDouble is also a handy utility for replacing, mocking dependencies, and verifying behavior.
没有测试,hapi项目将无法完成。 实验室和代码是hapi项目的首选测试库。 TestDouble还是一个方便的实用程序,用于替换, 模拟依赖关系和验证行为。
From the command line, install the following developer dependencies required for testing.
在命令行中,安装测试所需的以下开发人员依赖项。
npm install --save-dev code lab testdouble
Create a new folder in the root of the project named test
. Add a file to this folder named app.js
. In this file, add the following code.
在名为test
的项目的根目录中创建一个新文件夹。 将一个文件添加到名为app.js
的文件夹中。 在此文件中,添加以下代码。
"use strict";
const td = require( "testdouble" );
td.replace( "hapi-pino" );
require( "dotenv" ).config();
const { expect } = require( "code" );
const Lab = require( "lab" );
const app = require( "../src/app" );
const lab = exports.lab = Lab.script();
const { describe, it } = lab;
describe( "App", () => {
it( "home page returns valid response", async () => {
const server = await app.createServer( { port: 12345 } );
await server.initialize();
const res = await server.inject( {
url: "/",
method: "GET"
} );
expect( res.statusCode ).to.equal( 200 );
expect( res.result ).to.exist();
expect( res.result ).to.contain( "My first hapi server!" );
} );
} );
Modify your package.json
file, and change the test
scripts property to the following.
修改您的package.json
文件,并将test
脚本属性更改为以下内容。
"test": "lab -c"
Now, run the test from the command line using the following command.
现在,使用以下命令从命令行运行测试。
npm run test
The output from this first test should look similar to the following.
第一个测试的输出应类似于以下内容。
.
1 tests complete
Test duration: 67 ms
No global variable leaks detected
Coverage: 88.73% (16/142)
src/plugins/serverStart.js missing coverage on line(s): 8, 9
src/routes/index.js missing coverage on line(s): 17, 24, 35, 46-50, 64, 65, 69, 75, 77, 78
One of the great features of lab
is the ability to analyze your tests for code coverage. The code analysis report includes lines of your source code currently missing test coverage.
lab
一大特色是能够分析测试的代码覆盖率。 代码分析报告包含当前缺少测试覆盖范围的源代码行。
In this tutorial, you have learned the basics of creating a modern web application with hapi and some of the tools that are part of the hapi ecosystem. Below are more resources to explore.
在本教程中,您学习了使用hapi和hapi生态系统一部分的一些工具创建现代Web应用程序的基础。 以下是更多探索资源。
You can find the complete project source code on GitHub.
您可以在GitHub上找到完整的项目源代码 。
Thanks for reading, and happy... er... hapi coding!
感谢您的阅读,并高兴...呃... hapi编码!
翻译自: https://scotch.io/tutorials/build-a-secure-nodejs-application-with-javascript-async-await-using-hapi
node.js hapi