1. 服务器端基础概念
1.1 网站的组成
网站应用程序主要分为两大部分:客户端和服务器端
客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序,使用HTML、css、JavaScript构建
服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑
1.2 Node网站服务器
能够提供网站服务的机器就是网站服务器,能够接收客户端的请求,能够对请求作出响应
1.3 IP地址
互联网中设备的唯一标识
IP:Internet Protocol Address,互联网协议地址
1.4 域名
由于IP地址难以记忆,产生了域名的概念,域名就是平时上网时用的网址
虽然在地址栏中输入的是网址,但是最终还是会将域名转换为ip才能访问到指定的网站服务器
1.5 端口
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同服务
同一台服务器电脑上,可以提供不同的服务,比如web服务、邮件服务等等
1.6 URL
统一资源定位符,URL
传输协议://服务器IP或域名:端口/资源所在位置标识
http://www.itcast.cn/news/2018018/09152238514.html
但在平时输入网址的时候我们并没有输入端口,那是因为我们访问的是服务器的web服务,默认是80端口,浏览器会自动帮我们添加上去
http:超文本传输协议,提供了一种发布和接收HTML页面的方法
1.7 开发过程中客户端和服务器端说明
在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑
2. 在Node.js中创建web服务器
网站服务器实际上就是一台电脑,在这台电脑上我们要安装Node这个软件
要使用Node这个软件创建请求对象和响应对象
(1)需要用到系统模块http,需要先引入
(2)下面有一个createServer方法,作用是创建服务器对象,返回值就是服务器对象
(3)app.on(‘第一个参数‘, ‘第二个参数‘)
第一个参数是事件的名称,比如request
第二个参数是事件处理函数,当请求来的时候就会执行这个处理函数,函数中有两个参数,req和res
req是请求对象,存储了请求相关的一些信息,比如请求地址、请求ip
res是响应对象,使用这个对象下面的一些方法对客户端发来的请求做出响应
(4)这个服务器需要监听一个端口才能对外界的请求作出响应,才能向外界提供对应的服务:app.listen(端口号),一般可以写成3000,也可以写成其他端口号,只要这个端口号没有被占用即可
// 用于创建网站服务器的模块 const http = require(‘http‘); // app 对象就是网站服务器对象 const app = http.createServer(); // 当客户端有请求来的时候 app.on(‘request‘, (req, res) => {
// 结束这次请求,并响应了括号中的内容 res.end(‘<h2>hello,user</h2>‘); }); // 监听端口 app.listen(3000); console.log(‘网站服务器启动成功‘);
访问本机的服务器:域名:端口号 localhost:3000 (代表想要访问本机上3000端口提供的服务)
3. HTTP协议
3.1 HTTP协议的概念
超文本传输协议(HyperText Transfer Protocol, 缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准
3.2 报文
在http请求和响应的过程中传递的数据块就叫报文,包括要传送的数据考核一些附加信息,并且要遵守规定好的格式
3.3 请求报文
1. 请求方法
- GET 请求数据
- POST 发送数据
2. 请求地址
- req.headers 获取请求报文
req.headers[‘accept‘] 获取报文具体的某一项
- req.url 获取请求地址
- req.method 获取请求方法
// 获取请求地址 // req.url // console.log(req.url); // 根据客户端请求地址的不同,服务器端响应不同的内容 if (req.url == ‘/index‘ || req.url == ‘/‘) { res.end(‘welcome to homepage‘); } else if (req.url == ‘/list‘) { res.end(‘welcome to listpage‘); } else { res.end(‘not found‘); } // 获取请求报文信息 // req.headers console.log(req.headers); // 获取某一项的具体信息 console.log(req.headers[‘accept‘]); // 获取请求方式 // console.log(req.method); if (req.method == ‘POST‘) { res.end(‘post‘) } else if (req.method == ‘GET‘) { res.end(‘get‘) }
3.4 响应报文
1. HTTP状态码
- 200 请求成功
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
// 设置状态码 res.writeHead(500);
2. 内容类型
- text/html
- text/css
- application/javascript
- image/jpeg
- application/json
res.writeHead(200, { ‘content-type‘: ‘text/html;charset=utf-8‘ });
如果没有设置content-type这一项,就会默认设置为纯文本,如果响应的内容里面有HTML标签就不会显示出来,全部以纯文本的形式显示
res.end(‘<h1>welcome to homepage</h1>‘);
比如上面这行响应,会连标签一块显示出来,如果把content-type设置为text.html,他就可以正确显示为一级标题
charset=utf-8 作用是将文字正常显示,如果不写就会显示为乱码
4. HTTP请求与响应处理
4.1 请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器,比如登录操作
4.2 GET请求参数
参数被放置在浏览器地址栏中:例如:
参数获取需要借助系统模块url,url模块用来处理url地址
const url = require(‘url‘);
// 返回一个对象,把url的各个部分以对象的形式返回 // 参数一:要解析的url地址 // 参数二:将查询的参数解析成对象形式 true/false url.parse(参数一, 参数二)
如果输入的网址是 http://localhost:3000/index?age=20&username=ctt
url.parse(req.url, true)
返回以下内容:
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: ‘?age=20&username=ctt‘, query: [Object: null prototype] { age: ‘20‘, username: ‘ctt‘ }, pathname: ‘/index‘, path: ‘/index?age=20&username=ctt‘, href: ‘/index?age=20&username=ctt‘ }
通过下面的代码就可以解析出url中携带的参数
url.parse(req.url, true).query
如果我们使用 req.url来判断不同地址相应的的内容,如果携带了参数,根绝我们下面写的判断就会找不到匹配的地址,就会响应not found
if (req.url == ‘/index‘ || req.url == ‘/‘) { res.end(‘welcome to homepage‘); } else if (req.url == ‘/list‘) { res.end(‘welcome to listpage‘); } else { res.end(‘not found‘); }
这个时候我们就可以使用url对象中的pathname
使用解构赋值的方式获取到query和pathname,进而进行判断
// http://localhost:3000/index?age=20&username=ctt let { query, pathname } = url.parse(req.url, true); // 解构赋值 // 获取请求参数 console.log(query.age); console.log(query.username); // 获取请求路径 if (pathname == ‘/index‘ || pathname == ‘/‘) { res.end(‘<h2>welcome to homepage回家</h2>‘); } else if (pathname == ‘/list‘) { res.end(‘welcome to listpage‘); } else { res.end(‘not found‘); }
4.3 POST请求参数
- post请求参数被放在了请求报文(form data)中,get请求参数放在了url地址栏中
- 获取post参数需要使用data和end事件
- 使用querystring系统模块将参数转换为对象格式
// 用于创建网站服务器的模块 const http = require(‘http‘); // app 对象就是网站服务器对象 const app = http.createServer(); // 处理请求参数模块 const querystring = require(‘querystring‘); // 当客户端有请求来的时候 app.on(‘request‘, (req, res) => { // post 参数是通过事件的方式接受的 // data 当有请求参数传输的时候就会触发data事件 // end 当请求参数传递完成以后就会触发end事件 // post参数在理论上是可以无限的,post请求参数不是一次就接收完的 // 由于不是一次传递完的,需要把每次传递的参数拼接起来 let postParams = ‘‘; // params 当前传递过来的参数 req.on(‘data‘, params => { postParams += params; // 这里的postParams是一个字符串 }); req.on(‘end‘, () => { // 通过querystring.parse()将字符串转换为了对象格式 console.log(querystring.parse(postParams)); }); res.end(‘ok‘); }); // 监听端口 app.listen(3000); console.log(‘网站服务器启动成功‘);
4.4 静态资源
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、images文件
4.5 动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源
const http = require(‘http‘); const url = require(‘url‘); const path = require(‘path‘); const fs = require(‘fs‘); const mime = require(‘mime‘); const app = http.createServer(); app.on(‘request‘, (req, res) => { // 获取不带参数的请求路径 let pathname = url.parse(req.url).pathname; // 当地址栏中没有输入文件时,默认跳转到default.html pathname = pathname == ‘/‘ ? ‘/default.html‘ : pathname; // 将用户的请求路径转换为实际的服务器硬盘路径 let realPath = path.join(__dirname, ‘public‘ + pathname); // 获取请求文件的类型 let type = mime.getType(realPath); // 读取文件 fs.readFile(realPath, (error, result) => { // 如果文件读取失败 if (error !== null) { // 将中文正常显示出来,如果没有出现error是不需要写charset=utf-8的,因为HTML文件头部已经写了 res.writeHead(404, { ‘content-type‘: ‘text/html;charset=utf-8‘ }) res.end(‘文件读取失败‘); return; } res.writeHead(200, { // 如果不指定返回资源的类型,对于旧版本的浏览器可能就会出现问题 ‘content-type‘: type }) res.end(result) }); }); app.listen(3000); console.log(‘服务器启动成功‘);
5. Node.js 异步编程
5.1 同步API,异步API
同步API:只有当前API执行完成以后,才能继续执行下一个API
异步API:当前API的执行不会阻塞后续待拿的执行
5.2 同步API,异步API的区别(获取返回值)
同步API可以从返回值中拿到API执行的结果,但是异步API不可以
5.3 回调函数
自己定义函数让别人去调用
5.4 使用回调函数获取异步API执行结果
在异步API中,不能通过返回值得形式拿到执行结果,会默认返回undefined
5.5 同步API,异步API的区别(代码执行顺序)
同步API从上到下依次执行,前面代码会阻塞后面代码的执行
异步API不会等待API执行完成后再向下执行代码
5.6 Node.js中的异步API
回调地狱的问题:
const fs = require(‘fs‘); fs.readFile(‘./1.txt‘, ‘utf8‘, (err, result1) => { console.log(result1) fs.readFile(‘./2.txt‘, ‘utf8‘, (err, result2) => { console.log(result2) fs.readFile(‘./3.txt‘, ‘utf8‘, (err, result3) => { console.log(result3) }) }) });
5.7 Promise
解决Node.js异步编程中出现回调地域的问题