写一个NodeJs服务
创建node-service文件夹,初始化项目
bash
mkdir node-service
cd node-service
npm init -y
创建一个简单的HTTP服务器。以下是一个使用Node.js的核心模块http
来创建最基础的Node服务的示例:
javascirpt
const http = require('http');
// 创建服务器
const server = http.createServer((req, res) => {
// 设置响应头
res.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应内容
res.end('Hello, World!');
});
// 监听端口
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
写一个Express服务
创建express-service文件夹,初始化项目
bash
mkdir express-service
cd express-service
npm init -y
npm install express
创建一个简单的HTTP服务器。以下是一个使用Express来创建最基础的Node服务的示例:
javascirpt
const express = require('express');
// 创建Express应用程序
const app = express();
// 定义路由
app.get('/', (req, res) => {
res.send('Hello, World!');
});
// 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
相比于原生NodeJs,express增强了什么?
Express是一个基于Node.js的Web应用程序框架,它在原生的Node.js基础上提供了许多增强功能,使得构建Web应用更加简单和高效。下面是Express相对于原生Node.js的一些主要增强点:
1.路由处理:Express提供了简洁而强大的路由处理机制,可以轻松定义和管理各种路由。通过使用Express的路由功能,你可以根据不同的URL路径和HTTP方法来处理请求,使得代码结构更加清晰和可维护。
2.中间件支持:Express引入了中间件的概念,允许你在请求和响应之间插入功能模块。这使得处理请求的过程更加灵活,你可以通过添加中间件来处理身份验证、日志记录、错误处理等功能,而无需在每个路由处理程序中重复代码。
3.模板引擎:Express支持多种模板引擎,如EJS、Handlebars和Pug(以前称为Jade)。这使得在服务器端生成动态HTML页面变得更加方便,你可以使用模板引擎来组织和渲染页面,将数据动态地注入到模板中。
4.错误处理:Express提供了内置的错误处理机制,使得处理和捕获错误变得更加容易。你可以定义错误处理中间件来集中处理应用程序中的错误,从而提高代码的可读性和可维护性。
5.静态文件服务:Express可以轻松地为静态文件(如CSS、JavaScript和图像文件)提供服务。你只需指定静态文件所在的目录,Express将自动处理静态文件的请求,无需手动编写路由。
6.插件生态系统:Express拥有庞大的插件生态系统,你可以使用各种第三方插件来扩展Express的功能。这些插件提供了各种功能,如会话管理、身份验证、数据库集成等,帮助你更快地构建复杂的Web应用。
总的来说,Express提供了一种更加简洁、灵活和高效的方式来构建Web应用。它简化了开发过程,提供了许多有用的功能和工具,使得构建和维护Web应用变得更加容易。
路由处理方面,Express做了什么?
查看Express官方文档关于路由的指南,Express提供了get,post,all方法,匹配路由路径,route方法和router对象,方便我们编写路由相关的代码。我们看看原生Nodejs怎么处理路由:
javascirpt
const server = http.createServer((req, res) => {
// 获取请求的URL和方法
const url = req.url;
const method = req.method;
// 处理不同的路由和请求方法
if (method === 'GET' && url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!');
} else if (method === 'GET' && url === '/about') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('About Page');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
可以看出想实现跟Express一样的路由功能,使用nodejs会麻烦很多。
额外的,为了更好的处理路由,也就说处理请求-响应。Express增强了res和req对象。Express的req
对象具有许多有用的属性和方法,如req.params
用于获取路由参数,req.query
用于获取查询参数,req.body
用于获取请求体等。而Express的res
对象也提供了许多常用的方法,如res.send()
用于发送响应,res.json()
用于发送JSON响应,res.redirect()
用于重定向等。我们看看原生Nodejs怎么处理请求参数和给出响应:
javascirpt
// Nodejs获取路由参数、查询参数、请求体要写一下代码, 而express中可以直接从req对象中获取。
const parsedUrl = url.parse(req.url, true);
// routeParams数组包含了所有的路由参数
const routeParams = parsedUrl.pathname.split('/').slice(1);
// queryParams对象包含了所有的查询参数
const queryParams = parsedUrl.query;
let body = '';
req.on('data', (chunk) => {
body += chunk;
});
req.on('end', () => {
const requestBody = JSON.parse(body);
// requestBody对象包含了请求体参数
console.log(requestBody);
});
// 原生nodejs怎样实现express的res.json()
res.setHeader('Content-Type', 'application/json');
const jsonData = { message: 'Hello, world!' };
const jsonStr = JSON.stringify(jsonData);
res.end(jsonStr);
// 原生nodejs怎样实现express的res.redirect()
res.writeHead(302, {
'Location': 'https://example.com'
});
res.end();
中间件方面,Express做了什么?
我们分析一个中间件body-parser,在不用body-parser的情况下,获取请求体比较麻烦,还要自己解析请求体,但是使用中间件body-parser,再获取请求体就很简单。
javascirpt
// 无body-parser的情况
app.post('/api/user', (req, res) => {
let requestBody = '';
req.on('data', chunk => {
requestBody += chunk;
});
req.on('end', () => {
// requestBody 包含未经解析的请求体数据
console.log(requestBody);
// 在这里进行自定义的请求体解析和处理逻辑
// ...
res.sendStatus(200);
});
});
// 使用body-parser的情况
npm install body-parser
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/api/user', (req, res) => {
const user = req.body;
// 使用解析后的请求体数据进行处理
});
在Express官方文档开发中间件和使用中间件中对中间件有详细介绍,想深入了解可以查看。
Express对模板引擎的支持
我们可以通过设置模板引擎和res.render方法完成对模板的支持。
javascirpt
npm install ejs
// 设置模板引擎
app.set('view engine', 'ejs');
// 设置模板文件的存放位置
app.set('views', path.join(__dirname, 'views'));
app.get('/', (req, res) => {
const title = 'Express with EJS';
res.render('index', { title });
});
// views/index.ejs模板文件
<!DOCTYPE html>
<html>
<head>
<title>Express with EJS</title>
</head>
<body>
<h1>Welcome to <%= title %></h1>
</body>
</html>
如果想在nodejs中使用模板,代码如下:
ini
const fs = require('fs');
const ejs = require('ejs');
const template = fs.readFileSync('./views/index.ejs', 'utf8');
const renderedHTML = ejs.render(template, { title: 'My Page' });
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(renderedHTML);
res.end();
Express怎样做错误处理
抛出错误:当您在路由处理程序或其他中间件函数中遇到错误时,可以使用next
函数将错误传递给错误处理中间件。
javascirpt
app.get('/', (req, res, next) => {
// 模拟一个错误
const err = new Error('Something went wrong');
next(err);
});
// 创建一个专门用于处理错误的中间件函数,用于处理服务运行过程发生的异常
function errorHandler(err, req, res, next) {
// 处理错误逻辑
console.error(err);
// 设置响应状态码
res.status(500);
// 发送错误响应
res.send('Internal Server Error');
}
app.use(errorHandler);
Express怎样做静态文件服务
javascirpt
// 指定静态文件目录
app.use(express.static('public'));
// 在页面中有静态文件的请求,express就会在public文件夹找匹配的文件进行返回,很方便。
<link rel="stylesheet" href="/css/styles.css">
Express的插件生态系统
插件生态就是利用中间件机制,可以把很多服务功能封装成中间件供开发者使用。我们以后可以做深入的研究。
写一个Koa服务
javascirpt
const Koa = require('koa');
const app = new Koa();
// 定义路由
app.use(async (ctx) => {
ctx.body = 'Hello, World!';
});
// 启动服务器
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Koa和Express有什么区别?
Koa旨在"修复和替换NodeJs",而Express则是"增强NodeJs"。
接下来我来解释这句话。
比如请求和响应这两个对象,Express仍然沿用req和res,当开发者想获取请求参数时,Express使用body-parser中间件增强了req对象,使我们可以从req.params req.query req.body获取到相应参数。对于响应方法,一个res.end稍微难用,Express增加了一些res.send res.json方法,方便开发。这就是所谓的增强NodeJs。更近一步,Express支持路由、模板等功能,都是所谓的增强。
对比Koa,处理函数的参数不再是res,req。而是ctx,内部封装了request和response两个对象,request天然就支持参数获取,response.body就设置了响应体内容。同时,可以直接在ctx对象上使用request和response两个对象的方法和属性,这就是所谓的修复和替换NodeJs。更进一步,Koa的核心库并没有路由、模板等功能,这些功能的支持都需要引入如koa-router这种额外的库,也更加复合前端渐进式的思想潮流。
洋葱模型 vs 线性模型
Koa 使用的是洋葱模型(Onion Model)的中间件处理机制。洋葱模型的核心思想是将请求和响应通过中间件层层包裹,形成一个类似洋葱的结构。每个中间件在请求进入时执行一部分逻辑,然后将控制权交给下一个中间件,最后在响应返回时逆序执行剩余的逻辑。
洋葱模型有以下几个好处:
1.简洁明了:洋葱模型将请求和响应的处理逻辑分割成多个中间件,每个中间件只关注自己的逻辑,使得代码结构清晰、易于维护。
2.可复用性:中间件是独立的功能模块,可以在不同的应用程序中复用,提高代码的可复用性和可扩展性。
3.可扩展性:通过添加、删除或修改中间件,我们可以灵活地调整请求和响应的处理流程,实现不同的功能和需求。
4.异步流程控制:洋葱模型中的每个中间件都可以使用async/await
或返回 Promise 的方式处理异步操作,方便处理异步任务,例如数据库查询、网络请求等。
5.错误处理:洋葱模型在处理错误时非常方便,每个中间件可以通过try/catch
捕获错误并进行相应的处理,或者将错误传递给下一个中间件进行统一的错误处理。
总的来说,洋葱模型使得 Koa 的中间件处理变得灵活、可复用,并且能够更好地控制请求和响应的流程,提高开发效率和代码质量。
Koa怎样处理路由
javascirpt
npm install koa-router
const router = require('@koa/router');
const app = new Koa();
const router = new Router();
// 定义路由
router.get('/', async (ctx) => {
ctx.body = 'Hello, World!';
});
中间件方面,Koa做了什么?
这里看一个koa-cors插件的使用,跟Express也没有太明显的差异。
javascirpt
const cors = require('@koa/cors');
// 使用 koa-cors 中间件
app.use(cors());
其他方面
Koa在模板引擎、错误处理、静态文件服务方面,跟Express也大同小异,不再赘述。
总结
这篇文章跟大家一起初步认识了一下使用Nodejs写服务及其最流行的两个框架。未来我们还可以针对前端写Node服务进行更多的探索。