node1

什么是Node

Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时环境

Node环境的主要模块

 一. fs文件操作系统

:操作文件模块,提供一些操作文件的方法和属性。

1.1 fs.readFile 读取文件

// 导入fs模块
const fs = require('fs')
//readFile(目录,解码格式,读取结果的回调)
fs.readFile('./2.txt', 'utf-8', function (err, success) {
// 当读取失败时,err为一个包含其原因属性的对象;成功时为Null;以下操作可以试你辨别结果。
  if (err) {
    console.log('读取失败:', err.message)
  }
  console.log(success)
})

1.2 fs.writeFile 写入文件

注意:

1.重复写入会覆盖上次写入的内容;

2.只能创建写入的文件,不能创建写入的目录即必须事先创建好目录。

const fs = require('fs')
('写入路径',写入内容,写入回调)
fs.writeFile('./2.txt', 'helloNode', function (err) {
  if (err) {
    console.log('写入失败')
  }
  console.log('写入成功')
})

综合demo:

将源文件内容 :小明=98 小暗=100 小正=60,转换为以下格式并写入指定位置的文件中。

小明:98
小暗:100
小正:60

const fs = require('fs')
// 读取原数据
fs.readFile('./成绩案例.txt', 'utf-8', function (err, success) {
  if (err) {
    console.log('读取错误', err.message)
  }
  console.log('读出成功', success)
// 1.将其内容转换为数组;
  let oldArr = success.split(' ')
  let newArr = []
// 2.对每一项都进行替换操作,将替换完成项添至新数组。
  for (let i in oldArr) {
    newArr.push(oldArr[i].replace('=', ':'))
  }
// 3.将数组转换成字符回车分割('\r\n')
  let a = newArr.join('\r\n')
// 4.将字符写入到指定目录下
  fs.writeFile('./成绩处理完毕.txt', a, function (err) {
    if (err) {
      console.log('写入失败')
    }
    console.log('写入成功')
  })
})

2. 使用相对路径出现的路径拼接问题

   代码在运行的时候,会以执行 node 命令时所处的目录动态拼接,出被操作文件的完整路径

     如:在A\B\C该目录中打开终端

           如果我们在C目录下启用NodeJS文件通过./ 读取内容则拼接的目录为 : A\B\C\内容文件

           但如果我们在 \B目录启用 \C NodeJS文件通过./ 拼接的目录为 : A\B\内容文件        

解决:使用__dirname返回当前执行的JS文件目录,

         这样无论在哪执行Node命令都以当前Js的目录作为基准,以这个位置进行向上、下层的匹配。

fs.readFile(__dirname + './1.txt', 'utf-8', function (erro, success) {
  if (erro) {
    console.log('读取失败' + erro.message)
  }

 二. path路径模块

path 模块是 Node.js 官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。

2.1 路径拼接 path.join

path.join('路径片段','路径片段')
// 当前Js文件目录下的
const path = require('path')
const fs = require('fs')

// 注意 ../ 会抵消前面一层路径
// ./ 会被忽略
const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
console.log(pathStr) 		// \a\d\e

2.2 返回指定目录的文件名 path.basename(url,【扩展名】)

2.3 返回指定文件的扩展名 path.extname()

let url =  \a\b\lol.txt

console.log(path.basename(url)) // 'lol.txt'
 //basename(url, '.txt'),除拓展名返回
console.log(path.basename(url, '.txt')) // 'lol'

console.log(path.extname(url)) // .txt

fs.path综合案例:

将以下HTML文件的css、js进行抽离 => 通过linK href导入样式,通过script src导入js

<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>
  <style>
    #time {
      color: pink;
    }
  </style>
</head>

<body>
  <p id="time">look</p>
</body>
<script>
  let id = document.querySelector('#time')
  setInterval(() => {
    id.innerHTML = new Date()
  }, 1000);
</script>

</html>
const fs = require('fs')
const path = require('path')

// 1 设定匹配style和script内容的正则
let regStyle = /<style>[\s\S]*<\/style>/
let regJs = /<script>[\s\S]*<\/script>/

fs.readFile(path.join(__dirname, '/demo.html'), 'utf8', function (err, success) {
  if (err) { console.log('读取失败' + err) }
  getX({
    content: success,
    tag: 'style',
    resolveUrl: path.join(__dirname, '/html/index.css'),
    reg: regStyle
  })
  getX({
    content: success,
    tag: 'script',
    resolveUrl: path.join(__dirname, '/html/index.js'),
    reg: regJs
  })
  filterHtml(success)
})

//2.分解指定的内容并写入到指定位置。
let getX = function ({ content, tag, resolveUrl, reg }) {
  // 2.1过滤指定标签的内容
  let filterTagContent = reg.exec(content)
  // 2.2过滤内容中的标签
  let endContent = filterTagContent[0].replace('<' + tag + '>', ' ').replace('</' + tag + '>', ' ')
  // 2.3 写入最终的内容至指定位置   
  fs.writeFile(resolveUrl, endContent, function (err) {
    if (err) {
      console.log('写入失败')
    }
    console.log('写入成功')
  })
}

// 3.过滤Html将其内部的样式和JS为外部导入式
function filterHtml (content) {
  let html = content.replace(regStyle, "<link rel='stylesheet' href='./index.css'/>").replace(regJs, "<script src='./index.js'></script>")
  fs.writeFile(path.join(__dirname, '/html/index.html'), html, function (err) {
    if (err) {
      console.log('写入失败')
    }
    console.log('写入成功')
  })
}

三、http 模块

将本机转换为服务器,可以提供一些服务器功能

1、配置服务器的流程

//1.导入http模块
const http = require('http')
//2.创建服务器实例
const server = http.createServer()
//3.服务器绑定请求事件
server.on('request', function (req, res) {
  console.log(req.method)
  // req:客户端对象      
  let url = req.url
  let method = req.method
  console.log(`a requset : from:${url},method:${method}`)
  // 解决响应中文乱码问题
  res.setHeader('content-Type', 'text/html;charset=utf-8')
  // 4.设定响应
  res.end('服务是要💴的')
})
//5.启动801端口服务
server.listen(801, () => {
  console.log('server running at http://127.0.0.1:801')
})

2.设置不同的请求文件地址的响应值

let http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
  // 获取用户请求的文件地址
  let url = req.url
  let resContent = '404您请求的资源不存在'
  if (url == '/' || url == '/index.html') {
    resContent = '首页'
  }
  else if (url == '/about.html') {
    resContent = '关于'
  }
  res.setHeader('content-Type', 'text/html;charset=utf-8')
  res.end(resContent)
})
server.listen(201, () => {
  console.log('server running at http://127.0.0.1:201')
})

四、express模块

express模块基于HTTP模块所封装出的服务器模块能够极大的提高开发效率。

作用:

使用 Express,我们可以方便、快速的创建 Web 网站的服务器或 API 接口的服务器

··Web 网站服务器:专门对外提供 Web 网页资源的服务器。

·· API 接口服务器:专门对外提供 API 接口的服务器。

##基本使用

//1.导入内置express模块
const express = require('express')
//2.创建实例对象
const server = express()
//3.开启服务
server.listen(80, () => {
  console.log('服务端口已启动http://127.0.0.1')
})

监听POST和GET请求的方法

//参数1:请求的Url地址
//参数2:请求对应的处理函数

server.get('/getNaem', (req, res) => {
  // 用户在url地址后通过/key=value&key=value进行传递静态参数
  // 通过REQ.QUERY可以接受传递的静态参数
  res.send(req.query)
})
server.post('/getName', (req, res) => {
  res.send('ws')
})
//用户通过url/key/key传递 ':'后对应的动态参数
//通过req.params接受动态参数的值
server.get('/getName/:id/:name', (req, res) => {
  res.send(req.params)
})

静态资源托管

对HTTP模块的静态资源托管进行的封装,本质还是通过鉴定请求的url后根据设定路径响应资源。

如下:将file目录进行暴露,客户端可匹配该目录的内容。

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

// 在这里,调用 express.static() 方法,快速的对外提供静态资源
/*
   express.static(响应的资源目录前缀,映射目录)
*/
app.use('/files', express.static('./files'))
app.use(express.static('./clock'))

app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})

多静态资源部署

/*
    当部署多个目录的静态资源,不同目录中存在相同文件名时,匹配率先部署目录中的文件;后方目录文件不在进行匹配。
    如想分别匹配同名文件,需为url地址添加目录前缀名作为区分。
*/

//use(路径前缀,映射目录)
server.use('/html', express.static('./html'))
server.use('/helloworld', express.static('./helloworld'))

Express路由

在 Express 中,路由指的是客户端的求请服务器处理函数之间的映射关系。(根据请求执行对应的处理函数

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

路由的使用:

// 全局文件下挂载的路由
const express = require('express')
const app = express()

// 挂载路由*2
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')
})

由于我们可能设定多路由规则,故将路由进行抽离。

# routeModule.js

// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()

// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
  res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
  res.send('Add new user.')
})

// 4. 向外导出路由对象
module.exports = router

2.全局注册路由

#app.js
const express = require('express')
const app = express()

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

// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
app.use('/api', router)

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

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

路由的匹配规则:

服务器按照请求的先后顺序进行路由的匹配,匹配时需请求方法请求Url符合才会执行对应的处理函数。

为路由添加前缀

为该模块的路由统一添加前缀路径'/api',这样不同模块之间也可以存在相同路由规则。
server.use('/api', router.router)

路由中间件

中间件(Middleware ):特指业务流程的中间处理环节

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

全局中间件:

使用app.use()注册的中间件;

所有请求都进行该预处理


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

// 定义第一个全局中间件
app.use((req, res, next) => {
  console.log('调用了第1个全局中间件')
  //next 后 进入下一个处理
  next()
})
// 定义第二个全局中间件
app.use((req, res, next) => {
  console.log('调用了第2个全局中间件')
  next()
})

// 定义一个路由
app.get('/user', (req, res) => {
console.log('User page.')
  res.send('User page.')
})

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


/*
调用了第1个全局中间件
调用了第2个全局中间件
User page.
*/

局部中间件

不使用app.use()注册的中间件;

在路由处理函数单独使用的预处理函数

const express = require('express')
const router = express.Router()

//中间件处理函数必须在定义路由前定义
const a = function (req, res, next) {
  req.games = ['lol', 'cs']
  // next执行后才能进入下个处理
  next()
}
const b = function (req, res, next) {
  for (let i in req.games) {
    req.games[i] += '职业选手'
  }
  next()
}
// 局部多个中间件处理
router.post('/play', [a, b], (req, res) => {
  console.log('您的请求正在处理')
  res.send(req.games)
})

module.exports.router = router

错误中间件

当服务器发生错误时,错误中间件可以捕获错误。

注意:错误中间件注册与所有路由之后

router.post('/err', (req, res) => {
  throw new Error('服务器被黑客黑了')
})

// 错误中间件:捕获项目中出现的错误。
// 注意:需要放在所有配置路由后使用。
router.use((err, req, res, next) => {
  console.log(err.message)
  res.send(err.message)
  next()
})

内置中间件

第三方中间件

中间件的使用指南:

错误中间件注册在所有路由配置之后,其他中间件都应注册在设定路由之前

每个中间件函数处理完毕后都应next()进入下个处理函数。

所有中间件函数的req,res都是继承的,故上游中间件可以为下游中间件及最后的路由处理函数提供资源

请求资源跨域

由于三源协议导致浏览器只能请求同协议 、同地址、同端口号的服务器资源。

解决跨域

script 、link 、img 可以进行跨域请求并成功接收响应。

 1.jsonp处理

通过<Script>的src属性添加请求。

服务器将响应的值返还到<Script>内,从而实现跨域请求。

//# 服务端
const router = require('./接口router')
const express = require('express')
const server = express()

server.get('/api/jsonp', (req, res) => {
  var data = '111';
  //向Script标签中直接传输了111. 
  res.send(data)
})

server.use(cors())
server.listen(80, () => {
  console.log('服务已启动:http://127.0.0.1')
})
//客户端
<script src="http://127.0.0.1/api">
    // 在此会接受服务端发过来的111,但由于并不是一条可解析的语句从而语法错误
</script>

1.展现:服务发送的内容必须为js可解析的语句

调整

//# 服务端

const router = require('./接口router')
const express = require('express')
const server = express()

server.get('/api/jsonp', (req, res) => {
  var data = '111';
  let jsStr = `console.log(${data})`
  // 响应了一条固定的Js语句
  res.send(jsStr)
})

server.use(cors())
server.listen(80, () => {
  console.log('服务已启动:http://127.0.0.1')
})
//客户端
<script src="http://127.0.0.1/api">
    // console.log(111)
</script>

 2.展现:服务端写死返回的处理语句。

   调整

//# 服务端

const router = require('./接口router')
const express = require('express')
const server = express()

server.get('/api/jsonp', (req, res) => {
  var data = '111';
  // 响应客户端预先定义好的函数调用语句。
  res.send('sayHello()')
})

server.use(cors())
server.listen(80, () => {
  console.log('服务已启动:http://127.0.0.1')
})
//客户端
<script>
    function sayHello(){
        //自定义操作
    }
</script>


<script src="http://127.0.0.1/api">
    // 响应:sayHello()
</script>

3.展现 : 客户端预先定义函数,服务端返回对应的函数处理语句,

   缺点:  如果客户端处理函数名发生改变则服务器需同步匹配

  调整

//# 服务端

const router = require('./接口router')
const express = require('express')
const server = express()

server.get('/api/jsonp', (req, res) => {
// 客户端将函数名携带在请求里
  let fnName = req.query.fnName 
  let obj = {name:"ws"}
// 拼接返回的函数调用语句
  let jsStr = ` ${fnName}(Json.string(jsStr)) // logName({name:'ws'})  `
  res.send('sayHello()')
})

server.use(cors())
server.listen(80, () => {
  console.log('服务已启动:http://127.0.0.1')
})
// 客户端
<script>
    function logName(obj){
        console.log(obj.name)
    }
</script>


<script src="http://127.0.0.1/api?fnName = logName">
    // logName({name:'ws'})//'ws'
</script>

展现:  通过请求携带参数告诉服务器拼接的响应函数名 。

缺点:因为整个文档只有这么一个Script元素,所以解析器只会解析一次。

          客户端只会发送一次请求,而不能像按钮一样按需请求。

调整:

//客户端
<body>
  <button id="json">Json</button>
</body>

<script>
  let jsonBtn = document.querySelector('#json')
  // 1.点击时添加一个script实例,并且指定sr=''配置请求
  jsonBtn.addEventListener('click', () => {
    let s = document.createElement('script')
    s.src = 'http://127.0.0.1/api/jsonp?fnName=getDate'
    let body = document.querySelector('body')
    //2.将这个创造好的请求节点插入到文档中,通过请求节点重新发送请求
    body.appendChild(s)
    //3.由于点击一次就会添加一个请求节点,所以我们在请求节点使用后进行自灭
    body.removeChild(s)
  })
  //预留回调
  function getDate (data) {
    console.log(data)
  }
</script>

<script src="http://127.0.0.1/api/jsonp?fnName=getDate">

</script>
//# 服务端
const router = require('./接口router')
const express = require('express')
const server = express()

server.get('/api/jsonp', (req, res) => {
// 客户端将函数名携带在请求里
  let fnName = req.query.fnName 
  let obj = {name:"ws"}
// 拼接返回的函数调用语句
  let jsStr = ` ${fnName}(Json.string(jsStr)) // logName({name:'ws'})  `
  res.send('sayHello()')
})

server.use(cors())
server.listen(80, () => {
  console.log('服务已启动:http://127.0.0.1')
})

2.服务端cors配置

 使用 cors 中间件解决跨域问题 cors 是 Express 的一个第三方中间件

使用步骤

① 运行 npm install cors 安装中间件

② 使用 const cors = require('cors') 导入中间件

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

响应头配置

res.setHeader(Access-Control-Allow-字段名:value)
字段名作用可选值
-Origin允许访问该资源的外域 URL*(所有url) 、指定Url
-Headers对非官方额外的请求头进行声明       自定义 
-Methods除get、post、head方法外,指明实际请求所允许使用的 HTTP 方法。PUT、DELETE

简单请求

同时满足以下两大条件的请求,就属于简单请求:

请求方式:GET、POST、HEAD 三者之一

② HTTP 头部信息不超过以下几种字段无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、 Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-formurlencoded、multipart/form-data、text/plain)

预检请求

只要符合以下任何一个条件的请求,都需要进行预检请求:

① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型

② 请求头中包含自定义头部字段

③ 向服务器发送了 application/json 格式的数据

在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一 次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值