我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛
概览
用 nodeJs 搭建 web 服务器
实践
如果对于服务器不太了解,请参照文章 juejin.cn/post/709122…
用 nodeJs 搭建一个 web 服务器非常简单。安装完 node 后,我们在 d:\code\node 文件夹下新建一个 node.js 文件,写入如下代码:
const http = require('http');// 引入 node 自带的 http 模块
const server = http.createServer((request,response) =>{
response.writeHead(200,{'Content-Type':'text/plain'});
response.end('hello world')// 返回数据
})
server.listen(8000)//监听端口
console.log('server runing')
随后我们运行该 nodejs 文件。注意:我的 node js 文件放在 D:\code\node 下,所以我们需要限定为到该目录,然后运行该目录下的 node.js。
此时我们打开浏览器,访问 http://localhost:8000/,得到如下界面:
当当当,这个时候,一个简单的web服务器就搭建起来了,是不是很简单?
此时,相当于我们发起请求,服务器给我们返回了一个字符串。当我们在电脑上运行服务器软件时,相当于我们的电脑变成了一个服务器。当接收到请求时,我们可以返回任一一个电脑上的文件。
接下来,我们试试访问时,返回一个 index.html 文件。我们在 code/node 文件夹下新建如下index.html 文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1> 这是一个测试的 index.html 文件</h1>
</body>
</html>
然后我们在 node.js 写入如下内容:
const http = require('http');
const fs = require('fs');// 引入文件读写模块
const filepath = './index.html';// 指定文件路径
const server = http.createServer((request,response) =>{
response.writeHead(200,{'Content-Type':'text/html'});// 指定文件返回格式
fs.readFile(filepath,(err,data) =>{
if(err){
console.log('ERROR!Failed to read file!')
}
response.end(data.toString('utf-8'));// 读取文件并返回
})
})
server.listen(8000)
console.log('server runing')
重启启动服务器,打开浏览器,得到如下界面。
我们可以看到,我们通过浏览器向本地服务器发起请求的时候,它给我们返回了一个 html 文件。
如果从其他的客户端向启动的服务器发起请求,localhost 需要替换启动服务器的电脑的 ip 地址。
举一反三
一般来说,我们在浏览器上打开网页时,就是我们的浏览器向其他服务器发起了一个请求,得到一个 html 文件。而做数据操作时,则是返回更新后的数据。通过以上两个例子,我们可以说已经实现了最最基本的web服务器。
但是,在实际的开发项目中,我们需要处理不同类型的请求,(通过浏览器直接访问,发起的是 get 请求,一般来说处理数据使用 post 请求)。根据不同的请求路径返回不同的数据,还要做其他的逻辑处理。当然,我们可以使用 nodejs 做进一步的完善处理。但是,nodejs 发展至今,早就有前人写好了很多框架,封装好了重复的工作,让我们更加集中于业务处理。
所以,我们就站在巨人的肩膀上向前看吧。
基于 nodeJs 的 Web框架
目前主要有 Express 和 Koa 两种。此处不做详细介绍,如有兴趣,可以去官网查看。
本文主要基于 Koa 学习,因为 Koa 采用了 ES6 中的 Generator 函数 + yeild 语句 + Promise 语句 和 ES7 中的 async/await + Promise 来处理异步操作,比较符合我的开发习惯。
起一个 Koa 项目
我们安装完 node 后,使用 npm install koa -save 安装 Koa 即可。
然后我们使用 koa learn-koa 命令得到了一个 Koa 项目。
项目结构介绍
learn-koa
bin
www 服务器配置文件,包括引入http模块、监听端口号设置、错误处理
public 放置静态资源文件
images 放置图片
javascripts 放置 js 文件
stylesheets 放置 css 文件
routes 路由文件
index.js 默认、无前缀路由处理
users.js 针对 user 的路由处理
views 界面模板文件
app.js 项目配置文件,包括中间件、路由的配置等。
package.json 项目配置文件,包括依赖库、基本运行命令的配置等。
由于项目指定的 3000 端口在我的电脑上已经被占用,因此我将端口号改为:8003
启动路径概览
首先我们运行 npm install 下载相关依赖包。
目前为止,我们只下载了 koa。但是我们再 package.json 中 的 dependencies 中可以看到,项目还依赖 koa-json,koa-logger 等多个包。所以我们需要先将项目启动所需要的相关包都下载下来。
然后我们运行 npm run start 启动,得到如下界面。
此时,代表服务器已经被启动了。然后我们访问:http://localhost:8003/ 得到如下界面:
那么,这个过程中,发生了什么呢?
首先,从 npm run start 这个命令说起。我们不使用框架的时候,运行命令是 node xxx.js。这个应该比较好理解,我们需要在 node 环境下运行该文件,才能调用到 node 底层相关的模块。
而我们能使用 npm run start 来启动项目,是因为项目在 package.json 中指定了:
"start": "node bin/www",
这意味着,当我们运行 start 命令的时候,实际上运行的是对应命令,可以看到,还是我们熟悉的 node 格式。www 虽然没有 .js 后缀,但是我们使用 node 命令运行指定文件的时候,node 是读取文件内容,后缀的影响并不大。读者可以将上一节中 node.js 文件去掉后缀或者加上 .css 后缀,会发现node 服务器依然可以正常启动。
我们可以看到 bin/www 目录下引入了 app 文件,并通过 http.createServer(app.callback()) 加载 app 文件夹中对应的配置。
在 app 文件中,我们引入了 插件、路由、指定了 页面展示所用模板。
当我们访问的时候没有添加任何路径时,会匹配 routes->index 第一个处理。
即:
router.get('/', function *(next) {
yield this.render('index', {
title: 'Hello World Koa!'
});// 指定返回数据为一个 html 文件。
});
由于在 app.js 已经配置了如下代码:
app.use(views('views', {
root: __dirname + '/views',
default: 'jade'
}));
它制定了我们渲染页面时是渲染 views 文件夹下的。所以路由处理函数会指定处理 views/index.jade 文件夹。函数给 title 赋值为 'Hello World Koa!' ,index.jada 会获取该变量,相关中间件将 jade 文件转化为 html 格式,最终被浏览器正常读取,变成我们所看到的内容。
基础知识点概览
中间件
上文中我们提到了 中间件 这个概念。什么是中间件呢?
按我的理解,它其实就是一个处理函数。它接收参数,然后对这个参数做一些处理,比如把这个参数打印出来,然后再把接收到的参数返回出去。
比如 app 中的中间件使用:
app.use(views('views', {
root: __dirname + '/views',
default: 'jade'
}));
app.use(require('koa-bodyparser')());
app.use(json());
app.use(logger());
app.use() ,括号里跟的就是中间件。它就相当于说,app 先对 view 做了处理,然后使用了 koa-bodyparser 对返回值进行处理等等。
但是值得注意的是,中间件的调用不是 一个完成了然后调用另一个,而是这样的:
就比如我先调用一个 参数解析的 中间件,然后调用 打印日志的中间件,它是这样
参数解析中间件开始 - 打印日志中间件开始 - 答应日志中间件结束 - 参数解析中间件结束。
一路滑到底,然后往回滑。
this
this 在这里应该是指代上下文、当前环境。
this 中包含了 request。 即请求服务器时的所有信息。
包含了 params 属性。即包含的参数。
包含了 body 属性,即针对请求的返回内容。
我们可以在 routes/index.js 添加如下路由处理:
router.get('/test/:id', function *(next) {
console.log('this.params',this.params);// 将得到的参数格式打印出来
this.body = this.params.id;// 指定返回数据为 请求的 id
});
然后我们 ctrl + c 停止当前服务,npm run start 重新启动服务,在浏览器中输入:localhost:8003/test/5 得到如下界面:
控制台中打印数据如下:
可以看到 this 中确实包含 params 。
响应 GET 请求
浏览器直接访问默认为get请求,一般来说,get 请求是将参数带在链接后面,如果需要的传的参数较多,或者传的参数需要保密,则使用 post 请求。
项目中 routes 文件夹下的文件中,router.get 即处理get请求。
一般来说,我们请求 get 方式如下:
http://localhost:8003/test?username=wangsihui&age=25
此时,文件中处理如下:
router.get('/test', function *(next) {
console.log('this.request.query',this.request.query);
this.body = this.request.query.username;
});
运行之后,我们发现,控制台打印如下:
可以看到,后端已经获取了前端的 get 请求。而我们在浏览器中访问的时候,浏览器中展示内容:wangfuguier 。说明我们已经成功地拿到了请求的参数。
响应 POST 请求
post 请求是我们开发时常用的请求方式。
我们在 routes/index.js 添加如下代码:
router.post('/posttest', function *(next) {
console.log('响应 post 请求!');
const params = this.request.body;
console.log('params',params);
this.body = 'success';
});
node 本身并不提供对 post 请求的处理,但是我们可以 windows+R cmd,进入终端。输入如下命令模拟:
curl -X POST -H "content-type: application/json; charset=utf-8" http://127.0.0.1:8003/posttest -d {\"1\":\"2\"}
可以看到,请求端得到了回应。
服务端也响应了请求。
但是我们怎么才能拿到 post 请求的参数呢?项目已经配置了koa-bodyparser, 我们拿到的数据已经是一个对象格式,我们按照正常对象格式取值、设定返回值即可。
使用 JSON 文件完成增删改查
node 提供文件的读写功能。如果我们不懂数据库,我们了解了以上内容,我们可以在本地新建 json 文件,通过修改文件的数据来模拟一个简单的数据库,完成基础的增,完成数据的基础交互。
一般来说,在实际项目中,我们会使用数据库。俗话说,术业有专攻,使用专业的数据库软件,能够提供更快的数据修改、查找功能。这些内容,我们下一篇再谈。
建立 learn-koa/data/data.json 文件
删除 routes/user.js 原有内容,写入如下内容:
var router = require('koa-router')();
const fs = require('fs');// 引入文件读写模块
const filepath = '../learn-koa/data/data.json';// 指定文件路径.这里要注意下路径,可以看下报错
router.prefix('/users');
router.post('/addUser',function *(next){
// 获取用户数据
const _this = this;
const requestData = this.request.body;// 获取请求内容
fs.readFile(filepath,'utf-8',(err,data) =>{// 读取现有文件内容
if(err){
console.log(err);
console.log('ERROR!Failed to read file!')
}
const params = data && JSON.parse(data) || [];// 拿到数据转化
const newParams = [...params,requestData];// 将请求数据追加
fs.writeFile(filepath,JSON.stringify(newParams),(err)=>{// 写入新数据
if(err) _this.body = '写入文件失败!';
})
})
})
module.exports = router;
然后发起请求:
curl -X POST -H "content-type: application/json; charset=utf-8" http://127.0.0.1:8003/users/addUser -d {\"name\":\"Tom\"}
可以看到,data.json 文件中已经有如下内容:
[{"name":"Tom"}]
写入文件的数据就不会随着运行文件的关闭而消失啦,可以一直保存。
结语
到了这一步,如果你手里面有前端项目,其实已经可以完成基本的接口测试了。
本文代码git地址:https://gitee.com/is-wang-fugui-rich/learn-koa.git
下一节,让我们更加系统的学习后端所需知识,完成一个真正的后端吧~
如果对你有帮助,记得给我点赞哦。