nodejs day2

复习

了解:

  1. 浏览器渲染引擎工作原理
  2. 浏览器访问网站过程
  3. Web开发本质:请求、处理、响应
    • C/S 架构 和 B/S 架构
  4. node.js 是什么?
  5. node.js 有什么特点?
  6. node.js 安装
  7. node.js 开发网站 和 传统方式开发网站(PHP、JSP等)有什么区别?
  8. node.js REPL
    • 如何进入 REPL 环境
    • 如何退出 REPL 环境
  9. 通过创建 js 文件开发 node.js 程序

重点:

  1. 通过 fs 模块实现文件读写操作
  2. path 模块使用
  3. __dirname__filename
  4. node.js 中异步是如何实现的?为什么说 node.js 即是单线程又是异步非阻塞 I/O 模型?
    • 调用栈
    • 调用队列
  5. 编写简单的 http 服务程序,无论请求当前网站下哪个路径,都返回 hello world
    • 乱码问题。res.setHeader('Content-Type', 'text/plain; charset=utf-8');
    • 设置显示 HTML 内容。res.setHeader('Content-Type', 'text/html; charset=utf-8');

补充:

  1. 文件操作时,无需先判断文件是否存在,直接操作即可,如果文件不存在会反应在 error 对象中
  2. try-catch 的使用

今日授课内容

  1. 通过 node.js 编写 http 服务程序 - 通过读取静态 HTML 文件来响应用户请求(带图片和外部CSS样式)

  2. 通过 node.js 编写 http 服务程序 - 模拟 Apache 服务器处理静态资源

    • mime 第三方模块使用
  3. 在请求服务器的时候,请求的 url 就是一个标识!

  1. request(http.IncomingMessage) 和 response(ServerResponse) 对象介绍

    • request: 服务器解析用户提交的 http 请求报文,将结果解析到 request 对象中。凡是要获取和用户请求相关的数据都可以通过 request 对象获取
    • response: 在服务器端用来向用户做出响应的对象。凡是需要向用户(客户端)响应的操作,都需要通过 response 对象来进行。
  2. NPM

  3. package.json、package-lock.json 文件介绍

    • 元数据
  4. 自己设计路由,实现 HackerNews网站部分功能

    • underscore 模块介绍、url 模块介绍
  1. 在html页面中写相对路径’./‘ 和 绝对路径 ‘/‘的含义
    • 此处 ./ 相对的是吐出当前页面的url
  2. 通过设置 http 响应报文头实现弹框下载功能
    • 设置 Content-Type: application/octet-stream
    • 设置 Content-Disposition: attachment; filename=demo.txt

其他参考

在 html 网页中路径的含义

在 html 网页中相对路径 ‘./‘ 和 绝对路径 ‘/‘的含义

  1. “相对路径” 到底 “相对” 的是什么?
    • 相对当前请求的路径
    • 相对于吐出当前网页的路径

网页中的这个路径主要是告诉浏览器向哪个地址发起请求用的

  1. ‘./‘ 表示本次请求从相对于当前页面的请求路径(即服务器返回当前页面时的请求路径)开始
  2. ‘/‘ 表示请求从根目录开始

打开浏览器来演示,最终主要体现在了请求报文的 url 上。

演示步骤

  1. 找到之前的静态html页面中带有外部样式表连接的网页
  2. 请求该网页,查看http请求报文中的请求路径

一、通过静态html响应用户请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// 根据用户不同请求,做出不同响应(响应 现有的 HTML 文件)

// 加载 http 模块
var http = require('http');
// 加载 fs 模块
var fs = require('fs');
// 加载 path 模块
var path = require('path');


// 创建 http 服务,并启动该服务
http.createServer(function (req, res) {

// 通过 req.url 获取用户请求的路径,根据不同的请求路径服务器做出不同的响应
if (req.url === '/' || req.url === '/index') {
// 读取 index.html 文件
fs.readFile(path.join(__dirname, 'htmls', 'index.html'), function (err, data) {
if (err) {
throw err;
}

// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
} else if (req.url === '/login') {
// 读取 index.html 文件
fs.readFile(path.join(__dirname, 'htmls', 'login.html'), function (err, data) {
if (err) {
throw err;
}

// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
} else if (req.url === '/list') {
// 读取 index.html 文件
fs.readFile(path.join(__dirname, 'htmls', 'list.html'), function (err, data) {
if (err) {
throw err;
}

// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
} else if (req.url === '/register') {
// 读取 index.html 文件
fs.readFile(path.join(__dirname, 'htmls', 'register.html'), function (err, data) {
if (err) {
throw err;
}

// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
} else if (req.url === '/images/index.png') {
// 表示用户要请求 images 下的 index.png 图片
fs.readFile(path.join(__dirname, 'images', 'index.png'), function (err, data) {
if (err) {
throw err;
}

res.setHeader('Content-Type', 'image/png');
// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});

} else if (req.url === '/css/index.css') {
fs.readFile(path.join(__dirname, 'css', 'index.css'), function (err, data) {
if (err) {
throw err;
}

res.setHeader('Content-Type', 'text/css');
// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
} else {
// 读取 index.html 文件
fs.readFile(path.join(__dirname, 'htmls', '404.html'), function (err, data) {
if (err) {
throw err;
}

// 把读取到的 index.html 中的内容直接发送给浏览器
res.end(data);
});
}




}).listen(9090, function () {
console.log('http://localhost:9090');
});

二、模拟Apache

第一步 建立public文件夹把要用的html css 图片都放进去

第二步 下载安装第三方插件mime

  1. 打开 https://www.npmjs.com/package/mime
  2. 安装mime 命令:npm install mime (命令不一定是永久的还得看官网改变而改变)
    (2后面的方法均会因版本的变化而变化与官网为准)
  3. 引入外部模块 const mime = require(‘mime’);
  4. 使用方法 mime.getType(‘txt’);

第三步使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


// 加载 http 模块
var http = require('http');
var path = require('path');
var fs = require('fs');
var mime = require('mime');


// 创建服务
http.createServer(function (req, res) {
// 1. 获取用户请求的路径
// req.url
// /css/index.css
// /images/index.png


// 2. 获取 public 目录的完整路径
var publicDir = path.join(__dirname, 'public');

// 3. 根据 public 的路径 和用户请求的路径,最终计算出用户请求的静态资源的完整路径
var filename = path.join(publicDir, req.url);
// console.log(filename);

// 4. 根据文件的完整路径去读取该文件,如果读取到了,则把文件返回给用户,如果读取不到,则返回 404
fs.readFile(filename, function (err, data) {
// body...
if (err) {
res.end('文件不存在 404');
} else {

// 通过第三方模块 mime,来判断不同的资源对应的 Content-Type 的类型
res.setHeader('Content-Type', mime.getType(filename));
// 如果找到了用户要读取的文件,那么直接把该文件返回给用户
res.end(data);
}
});
// res.end('over');


}).listen(9090, function () {
console.log('http://localhost:9090');
});

三、url就是一个标示

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Index</title>
</head>
<body>
<h1 style="color: red;">Hello Index</h1>
<h1>你好 首页!!!看乱码吗?</h1>

<img src="./haha.xxx" alt="haha.xxx">//图片路径的变化

</body>
</html>

add.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34


// 加载 http 模块
var http = require('http');
var path = require('path');
var fs = require('fs');
var mime = require('mime');


// 创建服务
http.createServer(function (req, res) {

if (req.url === '/index.do' || req.url === '/index.html') {
// 读取 index.html 并返回

fs.readFile(path.join(__dirname, 'index.html'), function (err, data) {
if (err) {
throw err;
}
res.end(data);
});
} else if (req.url === '/haha.xxx') {
fs.readFile(path.join(__dirname, '150.jpg'), function (err, data) {
if (err) {
throw err;
}
res.setHeader('Content-Type', 'image/jpeg');
res.end(data);
});
}

}).listen(9090, function () {
console.log('http://localhost:9090');
});

四、request 对象 和 response对象

request 对象

  • request 对象类型 <http.IncomingMessage>, 继承自stream.Readable
  • request 对象常用成员
    • request.headers
    • request.rawHeaders
    • request.httpVersion
    • request.method
    • request.url

response 对象

  • response 对象类型 <http.ServerResponse>

  • response 对象常用成员

    • response.writeHead(statusCode[, statusMessage][, headers])
      1. This method must only be called once on a message and it must be called before response.end() is called.
      • 这个方法在每次请求响应前都必须被调用(只能调用一次)。并且必须在end()方法调用前调用
2. If you call response.write() or response.end() before calling this, the implicit/mutable headers will be calculated and call this function for you.
- 如果在调用writeHead()方法之前调用了write() 或 end()方法,系统会自动帮你调用writeHead()方法,并且会生成默认的响应头



3. When headers have been set with response.setHeader(), they will be merged with any headers passed to response.writeHead(), with the headers passed to response.writeHead() given precedence.
- 如果通过 res.setHeader() 也设置了响应头,那么系统会将serHeader()设置的响应头和writeHead()设置的响应头合并。 并且writeHead()的设置优先
1
2
3
4
5
// 示例代码:
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8',
'Content-Length': Buffer.byteLength(msg)
});
  • response.write(chunk[, encoding][, callback])

    • 参数1:要写入的数据,可以是字符串或二进制数据,必填
    • 参数2:编码,默认是utf8,选填。
    • 参数3:回调函数,选填。
  • response.end([data][, encoding][, callback])

    • 结束响应。
    • This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. The method, response.end(), MUST be called on each response.
    • res.end()这个方法告诉服务器所有要发送的响应头和响应体都发送完毕了。可以人为这次响应结束了。
    • 同时每次响应都必须调用该方法,用来结束响应
* 参数1:结束响应前要发送的数据,选填。
* 参数2:编码,选填。
* 参数3:回调函数,选填。
  • response.setHeader(name, value)

    • 设置响应报文头
  • response.statusCode

    • 设置或读取http响应码
  • response.statusMessage

    • 设置或读取http响应状态消息

五、NPM - Node Package Manager - Node 包管理器

NPM 是什么?

  • npm(全称Node Package Manager,即node包管理器)是Node.js默认的、以JavaScript编写的软件包管理系统。
  • npm 官方网站
  • npm 官方文档

一般当我们说npm的时候可能指3件事

  1. NPM 网站:https://www.npmjs.com/
  2. NPM 包管理库,存储了大量的JavaScript代码库
  3. NPM 客户端,我们所使用的npm命令行工具。使用JavaScript开发的基于node.js的命令行工具,本身也是Node的一个包。

参考图片

NPM

NPM

NPM 官方解释:

  • npm is the package manager for JavaScript and the world’s largest software registry.

    • npm 是一个JavaScript包管理器,并且是世界上最大的软件登记处
  • discover packages of reusable code — and assemble them in powerful new ways.

    • 发现可重用代码,并集成代码包到项目中的全新的、强大方式
  • npm makes it easy for JavaScript developers to share and reuse code, and it makes it easy to update the code that you’re sharing.
    • npm 让JavaScript开发者共享和重用代码变的更容易,同时也让我们更容易地更新正在被共享的代码

npm与 node.js

  • npm是Node.js默认的软件包管理系统。安装完毕node后,会默认安装好npm
  • npm本身也是基于Node.js开发的包(软件)

如何安装 NPM?

  • npm会随着Node.js自动安装,安装完毕node.js后会自动安装npm
  • 查看当前npm版本:npm -v
  • 更新npm:npm install npm@latest -g

NPM 使用

  1. https://www.npmjs.com/ 网站找到需要的包
  2. 在项目的根目录下,执行npm install 包名称安装
  3. 在node.js代码中通过 require('包名'); 加载该模块
  4. 注意:通过npm install 包名安装的包,会自动下载到当前目录下的node_modules目录下,如果该目录不存在,则创建,如果已存在则直接下载进去。
  5. 在代码中通过 require('包名'); 加载该模块

—– 上面说的这种方式叫做 本地安装。

NPM 全局安装介绍

  1. 什么是 npm 全局安装?
    • npm install 包名 -g npm 全局安装指的是把包安装成了一个命令行工具。
1
2
3
4
5
// 通过npm全局安装mime
npm install mime -g

//安装完毕后可以在命令行中直接使用
mime a.txt 命令来查看对应的结果
  1. npm 全局安装实际做了2件事:

    1. 下载包到一个指定的目录C:\Users\username\AppData\Roaming\npm\node_modules

    2. 创建一段命令行执行的代码。C:\Users\username\AppData\Roaming\npm\mime -> C:\Users\steve xiaohu zhao\AppData\Roaming\npm\node_modules\mime\cli.js

NPM 安装建议

  1. 全局安装只是为了可以当做命令行使用而已

五、npm常用命令介绍

  1. install,安装包。npm install 包名
  2. uninstall,卸载包。·npm uninstall 包名`
  3. version,查看当前npm版本。npm versionnpm -v

  4. init,创建一个package.json文件。npm init

  5. 注意:当使用 npm init -y 的时候,如果当前文件夹(目录)的名字比较怪(有大写、有中文等等)就会影响npm init -y 的一步生成操作,此时需要 npm init 根据向导来生成

“模块”(Modules)和”包”(Packages)的区别

  1. A module is any file or directory that can be loaded by Node.js’ require().
  • 模块可以是任何一个文件或目录(目录下可以有很多个文件),只要能被node.js通过require()即可。
  1. A package is a file or directory that is described by a package.json. This can happen in a bunch of different ways!
  • 包是一个文件或目录(目录下可以有多个文件)必须有一个package.json文件来描述,就可以是一个包。

node.js 错误调试:

  1. 当开启服务后,在浏览器中输入地址,如果出现浏览问题,首先要先看 服务器控制台是否报错。如果报错,直接根据服务器报错进行排错。

  2. 打开浏览器开发者工具中的 “网络” 部分,查看请求是否成功发出去了

  • 看一下请求报文是不是和我们想的一样
  • 响应状态码

六、package.json 文件

package.json 文件的作用?

  1. package.json 文件是一个包说明文件(项目描述文件),用来管理组织一个包(一个项目)
  2. package.json 文件是一个 json 格式的文件
  3. 位于当前项目的根目录下

元数据

package.json 文件中常见的项有哪些?

  • name
    • 包的名字
  • version
    • 包的版本
  • description
    • 包描述
  • author
    • 包的作者
  • main
    • 包的入口js文件,从main字段这里指定的那个js文件开始执行
  • dependencies
    • 当前包依赖的其他包

如何创建一个 package.json 文件

  1. 通过 npm init 命令 或者 npm init -ynpm init -yes 命令
  2. 手动创建一个

注意

  1. 通过 npm init -ynpm init -yes 创建 package.json 文件时,执行命令所在的目录接名称中不能包含大写字母
  2. package.json 文件中,项目名称本身不能包含大写字母
  3. npm 更新新版本后,项目所在的文件夹如果包含中文等特殊字符,创建的时候不会提示一步一步的输入,直接报错。

官方介绍

  1. package.json
  2. Using a package.json

七、自己设计路由实现 Hacker News 网站部分功能

参考网址:https://news.ycombinator.com/

步骤

  1. 实现新闻列表页 - 首页 - /index get
  2. 实现新闻详情页 - 详情页 - /details get
  3. 实现新闻添加页 - 提交页 - /submit get
    /add get
    /add post

  4. 实现保存数据功能 - 将数据写入到 data.json 文件中

  5. 实现首页数据的动态加载 - 根据.json文件来加载数据

实现思路

规划项目目录结构

  • HackerNews
    • resources
      • css
      • images
    • views(存放html模板页面)
    • data(保存新闻数据 data.json 文件)
    • app.js 文件(该文件即我们写服务器端JavaScript代码的地方,用来处理用户请求)
    • package.json

路由设计

  1. 注意:此处要自己设计路由,而不是像模拟 Apache 静态资源服务器一样

根据不同的请求返回相应的功能

  1. 当请求 //index 时,返回 views/index.html 文件内容
  2. 当请求 /details 时,返回 views/details.html 文件内容
  3. 当请求 /submit 时,返回 views/submit.html 文件内容
  4. 当请求 /add 时,保存用户提交的新闻数据,并将重定向到index页面。
  5. 对于其他以’/resources’开头的都当做静态资源来处理。

知识点

  1. 封装render()函数,将render()函数挂载到response对象上,实现response.render()效果。
  2. 使用underscore模块中的模板引擎功能,渲染index页面中的新闻数据。
  3. 通过 url 模块来处理 get 请求
1
2
3
4
5
6
7
8
// 1. 将 req.url 通过 url 模块来处理
var urlObj = url.parse(req.url, true);

// 1.1 获取用户请求的URL,不带查询字符串
// 注意:此时的reqUrl中不包含 get 的请求参数,只是pathname
var reqUrl = urlObj.pathname.toLowerCase();

// urlObj.query
  1. 服务器端接收 post 提交过来的数据
  2. 通过 querystring 模块将查询字符串转换为 json 对象

JSON在线格式化

JSON在线格式化

-------------本文结束感谢您的阅读-------------
0%