node.js学习笔记

学习路径:js基础语法+node.js内置api模块(fs、path、http等)+第三方api模块(express、mysql等)

常用node指令

1.在node.js环境中执行js代码

在想要执行的代码的目录下打开cmd,然后输入node+要执行的js文件的路径

终端里的快捷键

1.按 ↑ 即可快速定位带上一次执行的命令

2.使用tab键,能够快速补全路径

3.使用esc键,能够快速清空当前已输入的命令

4.输入cls+回车可清空终端

1.fs文件系统模块

用来操作文件的模块。

读取文件使用 fs.readFile() 方法,也可以使用同步的版本 fs.readFileSync()

fs.readFile(path[,options],callback)

options表示编码格式

const fs = require('fs')	//导入fs

fs.readFile('/Users/joe/test.txt', 'utf8' , (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})


。。。。。。。。。。。。。。。。。。。。。。。
const fs = require('fs')

try {
  const data = fs.readFileSync('/Users/joe/test.txt', 'utf8')
  console.log(data)
} catch (err) {
  console.error(err)
}

。。。。。。。。。。。。。。。。。。。。。。。。。。

可以通过指定标志来修改默认的行为:
fs.writeFile('/Users/joe/test.txt', content, { flag: 'a+' }, err => {})

fs.readFile() 和 fs.readFileSync() 都会在返回数据之前将文件的全部内容读取到内存中。

这意味着大文件会对内存的消耗和程序执行的速度产生重大的影响。

在这种情况下,更好的选择是使用流来读取文件的内容。

写入文件是使用 fs.writeFile() API,也可以使用同步的版本 fs.writeFileSync()

const fs = require('fs')		//导入fs

const content = '一些内容'

fs.writeFile('/Users/joe/test.txt', content, err => {
  if (err) {
    console.error(err)
    return
  }
  //文件写入成功。
})

。。。。。。。。。。。。。。。。。。。。。。。。。。。。

const fs = require('fs')

const content = '一些内容'

try {
  const data = fs.writeFileSync('/Users/joe/test.txt', content)
  //文件写入成功。
} catch (err) {
  console.error(err)
}

可能会使用的标志有:

  • r+ 打开文件用于读写。
  • w+ 打开文件用于读写,将流定位到文件的开头。如果文件不存在则创建文件。
  • a 打开文件用于写入,将流定位到文件的末尾。如果文件不存在则创建文件。
  • a+ 打开文件用于读写,将流定位到文件的末尾。如果文件不存在则创建文件。

在 Node.js 中使用文件夹

Node.js 的 fs 核心模块提供了许多便捷的方法用于处理文件夹。

检查文件夹是否存在

使用 fs.access() 检查文件夹是否存在以及 Node.js 是否具有访问权限。

创建新的文件夹

使用 fs.mkdir() 或 fs.mkdirSync() 可以创建新的文件夹。

处理路径问题

__dirname表示当前编写的文件所处的目录,用它来进行路径拼接就能保证移植性和维护性。

2.path路径模块

    • root: 根路径。
    • dir: 从根路径开始的文件夹路径。
    • base: 文件名 + 扩展名
    • name: 文件名
    • ext: 文件扩展名
  • path.relative()接受 2 个路径作为参数。 基于当前工作目录,返回从第一个路径到第二个路径的相对路径。
  • path.resolve()可以使用 path.resolve() 获得相对路径的绝对路径计算

3.http模块

//1.导入http模块
const http = require('http')
//2.创建web服务器实例
const server = http.createServer()
//3.为服务器实例绑定request事件,调用.on(),监听客户端的请求
server.on(request,(req,res)=>{
	console.log('someone visit our web server')
})
//4.启动服务器
server.listen(8080,function(){
	console.log('server running at http://127.0.0.1:8080')
})
const http = require('http')
const server = http.createServer()
server.on('request',(req, res)=>{
// 1.获取URL地址
const url = req.url
// 2.设置默认的响应内容为404 Not found
let content = "<h1>404 Not found!</h1>"
// 3.判断用户请求的是否为/或/index.html
// 4.判断用户请求的是否为/about.html
if(url === '/' || url ==='/index.html'){
    content = '<h1>首页</h1>'
}else if(url === '/about.html'){
    content = '<h1>关于页面</h1>'
}
// 5.设置Content-Type响应头,防止中文乱码
res.setHeader('Content-Type','text/html; charset=utf-8')
// 6.使用res.end()把内容响应给客户端
res.end(content)
})
server.listen(80,()=>{
    console.log('server running at http://127.0.0.1');
})

4.模块化

加载模块

// 1.加载内置的fs模块
const fs = require('fs')
// 2.加载用户的自定义模块
const custom = require('./模块化.js')
// 3.加载第三方模块
const moment = require('moment')

注:使用require()方法加载其他模块时,会执行被加载模块中的代码。

模块作用域

好处:防止全局变量污染的问题。

向外共享模块作用域中的成员

1.module对象

每个.js自定义模块中都有一个module对象,它里面存储了和当前模块相关的信息

2.exports对象(向外共享/暴露)

默认情况下,exports和module.exports指向同一个对象。最终的共享结果,还是以module.exports指向的对象为准。

exports和module.exports的使用误区

1.require()模块时,得到的永远是module.exports指向对象

注意:为了防止混乱,建议不要在同一个模块中同时使用exports和module.exports。

//1
exports.username = 'zs'
//为module.exports又赋值了一个新的对象
module.exports = {
  gender:'男',
  age:22
}

最终结果是{gender:'男',age:22}

//2
module.exports.username = 'zs'
//为exports又赋值了一个新的对象
exports = {
  gender:'男',
  age:22
}
最终结果是{usename:'zs'}

//3
//两个都没有赋值新的对象,指向的是同一个对象
exports.username = 'zs'
module.exports.gender='男'
最终结果是{username:'zs',gender:'男'}


//4
exports = {
  username : 'zs',
  gender:'男',
}
module.exports = exports
module.exports.age = '22'
最终结果是{username:'zs',gender:'男',age:22}

CommonJS规范

1.每个模块内部,module变量代表当前模块。

2.module变量是一个对象,它的exports属性是对外的接口。

3.加载某个模块,其实是加载该模块的module.exports属性。require()方法用于加载模块

5.npm与包

npm 是 Node.js 标准的软件包管理器。

node.js中的第三方模块又叫做包。

为什么需要包?

node的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发时,效率很低。

包是基于内置模块封装出来的,提供了更高级、更方便的API,极大的提高了开发效率。

格式化时间的两种做法

1.传统做法(先封装一个方法,在调用)

function dateFormat(dtStr){
  const dt = new Date(dtStr)

  const y = dt.getFullYear()
  const m = padZero(dt.getMonth()+1)
  const d = padZero(dt.getDate())

  const hh = padZero(dt.getHours())
  const mm = padZero(dt.getMinutes())
  const ss = padZero(dt.getSeconds())

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
// 定义补零的函数
function padZero(n){
  return n > 9 ? n : '0' + n
}

module.exports = {
  dateFormat
}
// 导入自定义的格式化时间的模块
const TIME = require('./dateFormat')

// 调用方法,进行时间的格式化
const dt = new Date()
// console.log(dt);
const newDT = TIME.dateFormat(dt)
console.log(newDT);

2.格式化时间的高级做法

1.使用npm包管理工具,在项目中安装格式化时间的包moment

2.使用require导入格式化时间的包

3.参考moment的官方API文档对时间进行格式化

// 导入需要的包
// 注意:导入的名称就是装包时候的名称
const moment = require('moment')
const dt = moment().format('YYYY-MM-DD HH:mm:ss')
console.log(dt);

包管理配置文件package.json

命令:npm init -y可以在执行命令所处的目录中,快速新建package.json文件。这个命令只能在英文的目录下成功运行,所以项目文件夹的名称不要使用中文,不能出现空格。

package.json中必须包含name(包的名字),version(版本号),main(包的入口)。

dependencies节点

专门同来记录使用npm i 命令安装了哪些包。

可以运行npm i 命令一次性安装所有的依赖包。

可以运行npm uninstall命令(没有简写),来卸载指定的包。

如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中。如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。

项目包:

npm i 包名 -D#开发依赖包(会被记录到devDependencies节点中的包,只在开发期间会用到)

npm i 包名#核心依赖包(被记录到dependencies节点中的包,只在开发期间和项目上线之后都会用到)

全局包:

在执行npm i 命令时,如果提供了-g参数,则会把包装安装为全局包。

cmd输入npm root -g即可查看全局包存储的位置。

安装全局包:npm i 包名 -g

卸载全局包:npm uninstall 包名 -D

注意:只有工具性质的包,才有全局安装的必要性。因为他们提供了好用的终端命令。

nrm

安装:npm i nrm -g

查看所有可用的镜像源:nrm ls

将下包的镜像源切换为taobao镜像:nrm use taobao

i5ting_toc

i5ting_toc是一个可以把md文档转为html页面的小工具。

使用:i5ting_toc -f 要转换的md文件路径 -o,-o表示转换完成后自动在浏览器中打开。

开发属于自己的包

写包:

配置package.json文件:

{
  "name": "mytools-wq",
  "version": "1.0.0",
  "main": "index.js",
  "description": "提供了格式化时间、HTMLEscape相关的功能",
  "keywords": ["dateFormat","escape"],
  "license": "ISC"

}

入口文件:

// 这是包的入口文件

const date = require('./src/dateFormat')
const escape = require('./src/htmlEscape')


// 向外暴露成员
module.exports = {
  ...date,
  ...escape
}
// 定义格式化时间的函数
function dateFormat(dateStr) {
  const dt = new Date(dateStr)

  const y = dt.getFullYear()
  const m = padZero(dt.getMonth() + 1)
  const d = padZero(dt.getDate())

  const hh = padZero(dt.getHours())
  const mm = padZero(dt.getMinutes())
  const ss = padZero(dt.getSeconds())

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
// 定义补零的函数
function padZero(n) {
  return n > 9 ? n : '0' + n
}
// 向外暴露成员
module.exports = {
  dateFormat
}
// 定义转义HTML字符的函数
function htmlEscape(htmlStr) {
  return htmlStr.replace(/<|>|"|&/g, (match) => {
    switch (match) {
      case '<':
        return '&lt;'
      case '>':
        return '&gt;'
      case '"':
        return '&quot;'
      case '&':
        return '&amp;'
    }
  })
}

// 定义还原HTML字符串的函数
function htmlUnEscape(htmlStr){
  return htmlStr.replace(/&lt;|&gt;|&quot;|&amp;/g,(match)=>{
    switch(match){
      case '&lt;':
        return "<"
        case '&gt;':
          return '>'
        case '&quot;':
          return '"'
        case '&amp;':
          return '&'

    }
  })
}
// 向外暴露成员
module.exports = {
  htmlEscape,
  htmlUnEscape
}

README.md文件中应包含:安装方式、导入方式、格式化时间、转义HTML中的特殊字符、还原HTML中的特殊字符、开源协议。

发布包

先注册一个npm账号然后终端登录,然后将下载包的路径由淘宝镜像改为原来的npm,然后将终端切换到包的根目录,

运行npm publish命令即可将包发布到npm上。

删除已发布的包

运行npm unpublish 包名 --force命令,即可从npm删除已发布的包。

注意:

1.这个命令只能删除72小时内发布的包

2.通过这个命令删除的包在24小时内是不允许被重复发布的

3.发包要慎重,尽量不发无意义的包

模块的加载机制

模块在第一次加载后会被缓存。这意味着多次调用require()不会导致模块代码被执行多次,它们会优先从缓存中加载,从而提高模块的加载效率。

内置模块的加载优先级最高。

加载自定义模块必须指定以./或../开头的路径标识符。如果没有路径标识符,那么node就会将它当做内置模块或第三方模块进行加载。

express

express的基本使用

app.get('请求URL',function(req,res){ /*处理函数*/})

app.post('请求URL',function(req,res){ /*处理函数*/})

res.send()方法,可以把处理好的内容,发送给客户端(把内容响应给客户端)

// Get 请求
app.get('/', (req, res) => {
	res.send('欢迎使用 Express(GET)');
});

// POST 请求
app.post('/', (req, res) => {
	res.send('欢迎使用 Express(POST)');
});

// Put 请求
app.put('/', (req, res) => {
	res.send('欢迎使用 Express(PUT)');
});

app.delete('/', (req, res) => {
	res.send('欢迎使用 Express(DELETE)');
});


// ... 等等其他请求方式


// 路径支持正则表达式
app.get('/a+b', (req, res) => {
	res.send('类似于 /ab、/aab、/aaab 等的路径均可访问');
})
// 1.导入express
const express = require("express")
// 2.创建web服务器
const app = express()

// 4.监听客户端的GET和POST请求,并向客户端响应具体的内容
app.get('/user',(req,res)=>{
  // 调用express提供的res.send()方法,向客户端响应一个JSON对象
  res.send({name:'zs',age:20,gender:'男'})
})
app.post('/user',(req,res)=>{
  // 调用express提供的res.send()方法,向客户端响应一个文本字符串
  res.send("请求成功")
})
app.get('/',(req,res)=>{
  // 通过req.query可以获取到客户端发送过来的查询参数
  // 注意:默认情况下,req.query是一个空对象
  console.log(req.query);
  res.send(req.query)
})
app.get('/user/:id',(req,res)=>{
  // req.params是动态匹配到的URL参数,默认也是一个空对象。
console.log(req.params);
res.send(req.params)
})
	

// 3.启动web服务器,调用app.listen(端口号,启动成功后的回调函数)
app.listen(80,()=>{
  console.log("express server running at http://127.0.0.1");
})

通过postman测试:

获取URL中携带的查询参数

app.get('/',(req,res)=>{
  // 通过req.query可以获取到客户端发送过来的查询参数
  // 注意:默认情况下,req.query是一个空对象
  console.log(req.query);
  res.send(req.query)
})

req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数。

获取URL中的动态参数

req.params对象,可以访问到URL中,通过 : 匹配的动态参数:

//id可以为其他的名称,但是:一定不可省,可以有多个动态参数
app.get('/user/:id',(req,res)=>{
  // req.params是动态匹配到的URL参数,默认也是一个空对象。
console.log(req.params);
res.send(req.params)
})

express中托管静态资源

1.express.static()

用法:app.use(express.static('public'))

const express = require('express')
const app = express()

// 在这里,调用express.static()方法,快速的对外提供静态资源
app.use(express.static('想要打开的文件目录'))
app.listen(80,()=>{
  console.log("express server running at http://127.0.0.1");
})

2.托管多个静态资源目录

如果要托管多个,多次调用这个函数就好了

app.use(express.static('public'))

app.use(express.static('files'))

访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需文件。

3.挂载路径前缀

如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可使用以下方式:

app.use('/public',express.static('public'))

nodemon

nodemon用来监视node.js应用程序中的任何更改并自动重启服务,非常适合用在开发环境中。

nodemon将监视启动目录中的文件,如果有任何文件更改,nodemon将自动重新启动node应用程序。

使用教程

1、全局安装

npm install -g nodemon

2、本地安装

npm install --save-dev nodemon

3、启动应用

nodemon [your node app]

4、使用帮助

nodemon -h 或者 nodemon --help

5、如果没有在应用中指定主机和端口,可以在命令中指定:

nodemon ./server.js localhost 8080

6、开启debug模式

nodemon --debug ./server.js 80

7. 帮助命令

tips:

如果nodemon用不了,执行如下操作:

1.以管理员身份运行powershell
2.输入set-ExecutionPolicy RemoteSigned
3.选择Y或者A

8、参考文档:

nodemon参考文档

express路由

在express中,路由指的是客户端请求与服务器处理函数之间的映射关系。

express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,

格式:app.MATHOD(PATH,HANDLER)

MATHOD——请求类型,PATH——请求的URL地址,HANDLER——处理函数

路由的匹配过程

每当一个请求到达服务器之后,需要先经过路由的匹配,只要匹配成功,才会调用相应的处理函数。

在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则express会将这次请求,转交给对应的function函数进行处理。

路由匹配的注意点:

1.按照定义的先后顺序进行匹配

2.请求类型和请求的URL同时匹配成功,才会调用对应的处理函数

路由的使用

1.把路由挂载到App(最简单的方法,但以后用到的不多)

const express = require('express')
const app = express()

// 挂载路由
app.get('/',(req,res)=>{
  res.send('hello world')
})
app.post('/',(req,res)=>{
  res.send('Post Request.')
})
app.listen(80,()=>{
  console.log('http://127.0.0.1');
})

2.模块化路由

步骤:

1.创建路由模块对应的.js文件

2.调用express.Router()函数创建路由对象

3.向路由对象上挂载具体的路由

4.使用module.exports向外共享路由对象

5.使用app.use()函数注册路由模块

// 这是路由模块
// 1.导入express
const express = require('express')
// 2.调用express.Router()函数创建路由对象
const router = express.Router()
// 3.向路由对象上挂载具体的路由
router.get('/',(req,res)=>{
  res.send('Get user list')
})
// 4.使用module.exports向外共享路由对象
module.exports = router

const express = require('express')
const app = express()

// 1.导入路由模块
const router = require('./router.js')
// 2.使用app.use()函数注册路由模块
app.use(router)

//注意app.use()函数的作用,就是来注册全局中间件

app.listen(80,()=>{
  console.log('http://127.0.0.1');
})

express中间件

express中间件的调用流程:当一个请求到达express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。

express的中间件,本质就是一个function处理函数。

中间件函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res。

var app = express();

// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

next函数的作用

next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。

const mw = function(req,res,next){
	
	console.log('这是最简单的中间件函数')
		
	//2把流转关系,转交给下一个中间件或路由
	next()
}

全局生效的中间件

通过调用app.use(中间件函数),即可定义一个全局生效的中间件

app.use(function(req,res,next){
	console.log('这是一个最简单的中间件函数')
	next()
})

中间件的作用

多个中间件之间,共享一份req和res。基于这样的特性,我们可以在上游的中间件中,统一为req和res对象添加自定义的属性和方法,供下游的中间件或路由使用。

app.use(function (req, res, next) {
  //  获取到请求到达服务器的时间
  const time = Date.now()
  // 为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
  req.startTime = time
  next()
})

app.get('/', (req, res) => {
  res.send('Home page.' + req.startTime)
})
app.post('/', (req, res) => {
  res.send('User page.' + req.startTime)
})

定义多个全局中间件:连续多次使用app.use()定义多个中间件就行了,客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用。

局部生效的中间件

不使用app.use()定义的中间件叫做局部生效的中间件

const mw = (req, res, next) => {
  console.log('调用了局部生效的中间件')
  next()
}

app.get('/', mw, (req, res) => {
  res.send('Home page.')
})
app.get('/user',  (req, res) => {
  res.send('User page.')
})

了解中间件的5个注意事项

1.一定要路由之前注册中间件

2.客户端发送过来的请求,可以连续调用多个中间件进行处理

3.执行完中间件的业务代码之后,不要忘记调用next()函数

4.next函数后面不要再写额外的代码

5.连续调用多个中间件时,多个中间件之间,共享req和res对象

中间件的分类

1.应用级中间件

应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(),其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。

var app = express();

// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

2.路由级中间件

绑定到express.Router()实例上的中间件,叫做路由级别的中间件。

var app = express();
var router = express.Router();

// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get('/user/:id', function (req, res, next) {
  // 如果 user id 为 0, 跳到下一个路由
  if (req.params.id == 0) next('route');
  // 负责将控制权交给栈中下一个中间件
  else next(); //
}, function (req, res, next) {
  // 渲染常规页面
  res.render('regular');
});

// 处理 /user/:id, 渲染一个特殊页面
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// 将路由挂载至应用
app.use('/', router);

3.错误处理中间件

错误处理中间件和其他中间件定义类似,只是要使用 4 个参数,而不是 3 个,其签名如下: (err, req, res, next)。顺序不能更换。必须注册在所有路由之后。

app.get('/', (req, res) => {
  // 人为制造错误
  throw new Error('服务器内部发生了错误!')
})

// 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err,req,res,next)=>{
  console.log('发生了错误'+err.message);
  res.send('Error:' + err.message)
})

4.内置中间件

常用的内置中间件:

1.express.static——快速托管静态资源的内置中间件,例如:HTML文件、图片、css样式等(无兼容性)

app.use()

2.express.json——解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)

app.use(express.json)

3.express.urlencoded——解析 URL-encode格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)

app.use(express.urlencoded({extended:false}))

5.第三方中间件

在express@4.16.0之前的版本,经常使用body-parse这个第三方中间件,来解析请求体数据

cookie 的中间件: cookie-parser

 npm install cookie-parser
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// 加载用于解析 cookie 的中间件
app.use(cookieParser());

自定义中间件

顾名思义,就是自己定义的中间件。就是在回调函数中写入自己需要的方法事件。

实现步骤:

1.定义中间件

2.监听req的data事件

3.监听req的end事件

4.使用querystring模块解析请求体数据

5.将解析出来的数据对象挂载为req.body

6.将自定义中间件封装为模块

//post参数解析中间件
function bodyParser(req,res,next){
    let arr = [];
    req.on("data", chunk => {
        arr.push(chunk);//将读取到的数据存储到数组中
    });
    req.on('end', () => {       //post 数据读取完毕
        let data = Buffer.concat(arr).toString();
        // console.log(qs.parse(data));
        req.body = qs.parse(data);//将读取到的post参数数据存储到request对象中body属性中
        next();//注意,一定要数据读取完毕之后,在顺延(转发)
    })
}
//自定义post参数解析中间件
app.use(bodyParser);

编写接口

//导入express
const express = require('express')
//创建服务器实例
const app = express()

//配置解析表单数据的中间件
app.use(express.urlencoded({extended:false})) 


// 导入路由模块
const router = require('./apiRouter.js')
// 把路由模块注册到app上
app.use('/api',router)

//启动服务器
app.listen(80, () => {
console.log('http://127.0.0.1')
})
const express = require('express')
const router = express.Router()

// 在这里挂载对应的路由

// get请求
router.get('/get', (req, res) => {
  // 通过req.query获取客户端通过查询字符串,发送到服务器的数据
  const query = req.query
  // 调用res.send()向客户端响应处理的结果
  res.send({
    status: 0, //0表示处理成功,1表示处理失败
    msg: 'GET请求成功!', //状态的描述
    data: query //需要响应给客户端的数据
  })
})

//post请求
router.post('/post', (req, res) => {
  // 1.获取客户端通过的请求体,发送到服务器的URL-encoded数据
  // 如果要获取URL-encoded格式的请求体数据,必须配置中间件app.use(express.urlencoded({extended:false}))
  const body = req.body
  // 2.调用res.send()方法,把数据响应给客户端
  res.send({
    status: 0,
    msg: 'post请求成功',
    data: body
  })
})
module.exports = router

CORS跨域资源共享

上面编写的接口,存在一个很严重的问题:不支持跨域请求。

解决跨域问题的方式:

1.CORS(主流你的解决方案,推荐使用)

2.JSONP(有缺陷的解决方案,只支持GET请求)

使用cors中间件解决跨域问题

1.运行npm i cors安装中间件

2.使用const cors = require("cors")导入中间件

3.在路由之前调用app.use(cors())配置中间件

什么是CORS

CORS (Cross-Origin Resource Sharing)(或通俗地译为跨域资源共享)是一种基于 HTTP响应 头的机制,

这些http响应头决定浏览器是否阻止前端js代码跨域获取资源。

该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

CORS的注意事项

1.CORS主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口。

2.CORS在浏览器中有兼容性,只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了CORS的服务端接口

CORS响应头

1.Origin:当前请求源,和响应头里的Access-Control-Allow-Origin 对标, 是否允许当前源访问,Origin是不可修改的

2.Access-Control-Request-Headers:本次真实请求的额外请求头,和响应头里的Access-Control-Allow-Headers对标,是否允许真实请求的请求头

3.Access-Control-Request-Method:本次真实请求的额外方法,和响应头里的Access-Control-Allow-Methods对标,是否允许真实请求使用的请求方法

app.all('/cors-server',(request,response) => {
    //设置响应头
	//以下都是允许所有的http请求方法
    response.setHeader("Access-Control-Allow-Origin",'*');
    response.setHeader("Access-Control-Allow-Headers",'*');
    response.setHeader("Access-Control-Allow-Method",'*');
    response.send('hello CORS');
})

cors的两种请求

1、简单请求(满足以下条件):

请求方式为HEAD、POST 或者 GET

http头信息不超出一下字段:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)

为什么要分为简单请求和非简单请求,因为浏览器对这两种请求方式的处理方式是不同的。

2、预检请求 :

非简单请求是那种对服务器有特殊要求的请求,请求方法不是HEAD、POST 或者 GET,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

这两个,其实一个是另外一个的对立面。而且最大的不同在于,简单请求只发起一次网络请求。

而预检请求会发起两次,先发起一个option请求后,才会发起它真正的请求!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值