学习Node.js的第一天
- 一、初步认识nodejs
- --------------------------------------------------------淘宝镜像源
- ------------------------------解决require没有自动补齐
- 二、fs模块
- 三、path模块
- 四、http模块
- 五、模块化代码
- 六、express模块
- 七、MySQL
- 八、在项目中操作MySQL
- 九、web开发模式
一、初步认识nodejs
--------------------------------------------------------淘宝镜像源
npm config set registry https://registry.npm.taobao.org
------------------------------解决require没有自动补齐
npm install -D tslib @types/node
1.1nodejs简介
Node.js 在浏览器之外运行 V8 JavaScript 引擎(Google Chrome 的内核)。 这使得 Node.js 的性能非常好。
node.js是一个独立的运行环境,无法调用DOM和BOM浏览器内置API
检测nodejs是否安装成功cmd中node -v
二、fs模块
2.1读取指定文件中的内容
无论读取成功失败都会执行回调函数,第一个形参代表失败的结果,第二是成功的结果
//路径 编码(可选参数) 回调函数
fs.readFile(path,[options],callback)
读取文件
const fs = require('fs');
fs.readFile('./01fs读取文件中的内容.js','utf-8',function (err,data){
console.log(err);//失败
console.log(data);//成功
})
2.2在文件中写入内容
// 文件路径 写入的内容 编码(可选参数) 回调函数
fs.writeFile(file,data[options],callback)
指定路径写入文件
const fs = require('fs');
const data =' 123456'
fs.writeFile('./1.txt',data,function (err){
if(!err){
console.log('文件写入成功')
}
})
2.3整理成绩小案例
const fs = require('fs');
fs.readFile('./03成绩.txt','utf-8',function (err,data){
if(!err){
console.log('文件读取成功'+data);
}else {
console.log('文件读取失败'+err);
}
//1.将文件里面的数据以,进行分割
const strdata1 = data.split(','); //这里会自动生成一个数组来装数据,
console.log(strdata1)
//2.弄一个新数组来装 咱们把数据里面的等号用冒号替换
const strdata2 = [];
strdata1.forEach(item => {
strdata2.push(item.replace('=',':'));
})
console.log(strdata2);
//3.将文件的里面的每一个数据弄成一行显示
const newdata = strdata2.join('\r\n');
console.log(newdata);
fs.writeFile('./03成绩导出.txt',newdata,function (err){
if(!err) {
console.log('文件写入成功')
console.log(data);
}
})
})
文件前后对比
2.4路径问题
我们在使用fs模块的时候如果有./或者…/这种相对的路径的时候
我们在使用node 运行js文件的时候会出现路径动态拼接错误
这里出现的错误就是我们使用node执行这个js文件时是以当前路径去查找
我们如果使用/是不会出现这种问题的
也开以提供一个完整的文件存放路径(当然这种操作会被开除)
我们要__dirname(表示当前文件所处的目录)
在写入文件时候一样会出现文件动态路径拼接问题
所以我们使用__dirname
三、path模块
3.1多个路径拼接在一起
path.join()方法可以拼接路径,以后我们也可以使用这个方法在文件读写上进行路径拼接。顺便提一下__filename
3.2获取路径中的最后一部分
path.basename()方法,获取路径的文件名部分
const path = require('path');
const str = '/a/b/c/index.js';
const str1 = path.basename(str);
console.log(str1);
const str2 = path.basename(str,'.js'); //干掉后缀js
console.log(str2);
3.3获取路径中的文件扩展名
path.extname获取扩展名
const path = require('path');
const str = '/a/b/c/index.js';
console.log(path.extname(str));
3.4分析需求,读取文件内容
将一个html网页分成css和html和js三个部分
//将我们的old1.html这个文件中的css和body和js三个模块中的内容提取出来
const fs = require('fs');
const path = require('path');
// \s空白字符 \S非空白字符 *匹配任意次 <后面的\是转义字符防止/和最后的/冲突
const style=/<style>[\s\S]*<\/style>/;
const script = /<script>[\s\S]*<\/script>/;
//1.读取整个html文件
fs.readFile(path.join(__dirname,'file/old1.html'),'utf-8',function (err,data){
if(err){
console.log('读取文件失败');
}else{
resolveCss(data);
resolveJs(data);
resolveHtml(data);
}
})
function resolveCss(css){
// exec() 方法用于检索字符串中的正则表达式的匹配。
// 如果字符串中有匹配的值返回该匹配值,保存在数组下标0里面,否则返回 null。
const r1 = style.exec(css);
//我们把开始和结束两个标签替换成空格
const newr1 = r1[0].replace('<style>','').replace('</style>','');
fs.writeFile(path.join(__dirname,'/file/1.css'),newr1,function (err){
if(!err){
console.log('css保存成功')
}
})
}
function resolveJs(js){
// exec() 方法用于检索字符串中的正则表达式的匹配。
// 如果字符串中有匹配的值返回该匹配值,保存在数组下标0里面,否则返回 null。
const r1 = script.exec(js);
//我们把开始和结束两个标签替换成空格
const newr1 = r1[0].replace('<script>','').replace('</script>','');
fs.writeFile(path.join(__dirname,'/file/1.js'),newr1,function (err){
if(!err){
console.log('js保存成功')
}
})
}
function resolveHtml(html){
//对style和script两个标签里面的内容做一个替换
const newhtml = html.replace(style,'<link rel="stylesheet" href="1.css">')
.replace(script,'<script src="1.js"></script>');
fs.writeFile(path.join(__dirname,'/file/1.html'),newhtml,err => {
if(!err){
console.log('1.html文件保存成功');
}
})
}
四、http模块
4.1什么是http模块
在网络节点中,负责消耗资源的叫客户端。负责对外提供资源的叫服务器。
4.2进一步理解http模块的作用
服务器和普通电脑的区别在于,服务器上安装了web服务器软件
例如IIS、Apache,通过这些服务器软件,就能把一台普通的电脑变成一台web服务器
4.3ip地址就是互联网上每台电脑唯一的地址,采用点分十进制。
127.0.0.1对应的域名是localhost,都代表我们自己的电脑
端口号的作用
4.4使用node创建最基本的web服务器
//引入http模块
const http =require('http');
//为服务器创建实例
const app = http.createServer();
//为服务器绑定request事件,监听客户端请求
app.on('request', (req,res) => {
console.log('hello world');
})
//设置端口号,启动服务器
app.listen(3000,function (){
console.log('服务器启动成功~');
})
启动服务器之后,访问这个127.0.0.1:3000这个域名就会打印hello world
4.4.1req请求对象
req请求对象包含了客户端的相关数据和属性
url是端口号后面的数据
这条代码写在app.on监听客户端请求里面
console.log(
你的请求url地址${req.url},你的请求方式是${req.method}
);
4.4.2res响应对象
res.end向客户端发送指定内容,并结束这次请求的处理过程。
const str = `Your request url is ${req.url},and method is ${req.method}`;
res.end(str);
4.4.3设置响应头解决中文乱码
app.on('request', (req,res) => {
const str = `你的请求url地址${req.url},你的请求方式是${req.method}`;
//响应头设置Content-Type的值为text/html;charset=utf-8 可以解决中文乱码
res.setHeader('Content-Type','text/html;charset=utf-8');
res.end(str);
})
4.5根据不同的url来响应不同的html页面
const http =require('http');
const app = http.createServer();
app.on('request', (req,res) => {
let content = '<h1>404找不到网页</h1>';
const url = req.url;
if (url === '/'){
content= '<h1>欢迎访问首页</h1>'
}else if(url === '/about.html'){
content= '<h1>欢迎访问更多页面</h1>'
}
res.setHeader('Content-Type','text/html;charset=utf-8');
res.end(content);
})
app.listen(3000,function (){
console.log('服务器启动成功~');
})
4.6实现网页响应的web服务器
const fs = require('fs');
const path =require('path');
const http =require('http');
const app = http.createServer();
app.on('request', (req,res) => {
let content = '<h1>404找不到网页</h1>';
const url =req.url;
//把请求的url地址映射为具体的文件路径
fs.readFile(path.join(__dirname,'/file',url),'utf-8',(err,data) => {
if(err) return res.end(content);
content=data;
res.end(content);
})
})
app.listen(3000,function (){
console.log('服务器启动成功~');
})
五、模块化代码
5.1模块的分类require的使用
加载别的模块,会执行别的模块里面的代码
5.2模块作用域和module
5.2.1模块作用域
模块作用域和函数作用域差不多,在模块自定义的变量、方法等成员,只能在当前模块访问,别的模块访问不到的
好处就是防止全局变量污染,例如你导入的两个模块对同一个变量名定义了不同的值
5.2.2module对象
在每个自定义的js文件中都有module对象,他储存l当前模块的有关信息
moudle.exprots可以使用这个将模块的成员共享出去
require导入的自定义模块,就是这个moudle.exports对象
使用require导入模块的时候,导入的结果永远以module.exports指向的对象为准
可以这样理解前面两个是给对象添加属性,后面的那个是给对象重新赋值
module.exports和exprots指向同一个对象,因为这里两个都是空对象
5.2.3使用误区
使用require导入的时候,永远得到的是module.exports指向的对象
误区一
exports.name='张山';
module.exports={
nikename:'小黑',
age:18
}
console.log(exports);
console.log('----------')
console.log(module.exports)
console.log('----------')
console.log(module.exports === exports);
误区二、
误区三、这里与一不同的是这个module.exports并没有指向一个对象
误区四、
误区四张图
5.3CommonJs介绍
5.4包介绍
5.4.1安装所有包
npm install
这个命令会读取package.json的依赖包并且全部下载
5.4.2卸载指定包
npm uninstall
这个命令会卸载所有的包
第三方的模块又叫包。
包也是基于内置模块封装出来的,是为了提供我们的开发效率。
最大的包网站npmjs.com.
5.5包配置文件管理
dependencies节点
如何判断我们的包项目上线之后会不会用到,其实npmjs.com在安装的代码就会告诉我们
5.6解决下包慢的问题
原因分析
解决方案
// 配置npm代理来提高速度,设置淘宝镜像
npm config set registry https://registry.npm.taobao.org
// 查看配置是否成功
npm config get registry
5.6.1查看和切换下包镜像源
5.7npm与包(md文件的转换)
全局包安装的位置
C:\Users\A\AppData\Roaming\npm\node_modules
将md文件转换为html的小工具 -o是转换完成后在浏览器自动打开
规范包的使用
5.8模块加载机制
注意:内置模块加载优先级是最高的
六、express模块
6.1express的基本操作
安装express
npm i express
const express = require('express');
const app = express();
app.listen(30000,() => {
console.log('服务器启动成功');
})
6.2监听GET和POST请求
GET请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
<script>
function main(){
const xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:3000/get?a=200');
xhr.send();
//on when 当什么时候
//readstate 是 xhr中的属性 表示状态 0 1 2 3 4
//change 改变
xhr.onreadystatechange = function (){
//判断服务器响应了所有的结果
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
console.log(xhr.response);
}
}
}
}
main();
</script>
POST请求(请求字符串和url中的动态参数)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
<script>
function main(){
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:3000/post/1?a=200');
xhr.send();
//on when 当什么时候
//readstate 是 xhr中的属性 表示状态 0 1 2 3 4
//change 改变
xhr.onreadystatechange = function (){
//判断服务器响应了所有的结果
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
console.log(xhr.response);
}
}
}
}
main();
</script>
多个请求字符串使用&连接
多个请求url中的动态参数
xhr.open('POST','http://127.0.0.1:3000/post/1/houwang');
app.post('/post/:id/:name',(request,response) => {
6.3托管静态资源
express.static()方法创建静态服务器资源
通过这个代码就可以把目录里面的文件对外开放访问
const express = require('express');
const app = express();
//调用express.static方法,快速提供静态资源
app.use(express.static('./file'));
app.listen(3000,() => {
console.log('服务器启动成功')
})
访问静态资源文件的时候,按express.static()方法添加顺序去目录查找
挂载路径前缀
const express = require('express');
const path = require('path');
const app = express();
//调用express.static方法,快速提供静态资源
app.use('/05express',express.static(path.join(__dirname,'../05express模块')));
app.listen(3000,() => {
console.log('服务器启动成功')
})
6.4自动重启服务器
nodemon安装参考学习AJAX的第一天
6.5express中的路由
路由客户端的请求和服务器处理函数之间的映射关系
路由三部分:请求类型,url地址,处理函数
路由的模块化
const express = require('express');
const router = express.Router(); //创建路由对象
router.get('/router-get',(request,response) => {
response.send('hello world');
})
module.exports = router; //暴露出去
添加路由前缀,一样的是上个js代码
const express = require('express');
const router = require('./05router模块化');
const app = express();
//app.use(); 都是用来注册全局中间件的
app.use('/router',router);
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.6express中间件介绍
中间件特指业务流程的中间处理环节
中间件的调用流程
中间件的流程
中间件函数的形参列表中必须包含next参数,路由只需要req和res
next函数的作用
6.7express中间件的初体验
6.7.1初体验
const express = require('express');
const app = express();
const nw = function (request,response,next){
console.log('最简单的中间件');
//流转关系,转交给下一个路由或者中间件
next();
}
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.2全局中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫全局中间件。
const express = require('express');
const app = express();
const nw = function (request,response,next){
console.log('最简单的中间件');
//流转关系,转交给下一个路由或者中间件
next();
}
//将nw注册为全局中间件
app.use(nw);
app.get('/',(requset,response) =>{
response.send('hello world')
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.3全局中间件的简化形式
const express = require('express');
const app = express();
app.use((request,response,next) => {
console.log('最简单的中间件');
//流转关系,转交给下一个路由或者中间件
next();
})
app.get('/',(requset,response) =>{
response.send('hello world')
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.4中间件的作用
比如我们的每个路由都需要拿到一个时间
const express = require('express');
const app = express();
app.use((request,response,next) => {
const startTime = new Date();
request.time = startTime;
//流转关系,转交给下一个路由或者中间件
next();
})
app.get('/',(requset,response) =>{
response.send('hello world /'+requset.time)
})
app.get('/user',(requset,response) =>{
response.send('hello world user'+requset.time)
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.5局部中间件
const express = require('express');
const app = express();
const nw = (request,response,next) => {
console.log('这是一个局部中间件函数');
next();
}
//nw只在这个路由里面生效
app.get('/', nw,(requset,response) => {
response.send('hello world /');
})
app.get('/user',(requset,response) =>{
response.send('hello world user');
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.6多个局部中间件
const express = require('express');
const app = express();
const nw1 = (request,response,next) => {
console.log('这是第一个局部中间件函数');
next();
}
const nw2 = (request,response,next) => {
console.log('这是第二个局部中间件函数');
next();
}
//nw只在这个路由里面生效
app.get('/', [nw1,nw2],(requset,response) => {
response.send('hello world /');
})
app.get('/user',(requset,response) =>{
response.send('hello world user');
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.7.7中间件的五个注意事项
6.7.8中间件的分类
1.应用级别的中间件
2.路由级别的中间件
3.错误级别的中间件
4.express的内置中间件
使用request.body来接受客户端发送的请求数据
5.第三方中间件
6.8自定义中间件
6.8.1request监听data和end事件
const express = require('express');
const app = express();
app.use((request,response,next) => {
//定义字符串用来存储
let str = '';
//使用.on绑定data事件 使用chunk来接受请求端的数据
request.on('data',chunk => {
str+=chunk;
});
//接受完客户端发送过来的数据触发
request.on('end',() => {
console.log(str);
})
})
app.post('/',(requset,response) =>{
//设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
response.send('hello world /')
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.8.2使用querystring模块解析请求体数据
const qs =require('querystring');
const body = qs.parse(str);
6.8.3将解析出来的数据对象挂载到路由中
const express = require('express');
const qs =require('querystring');
const app = express();
app.use((request,response,next) => {
//定义字符串用来存储
let str = '';
//使用.on绑定data事件 使用chunk来接受请求端的数据
request.on('data',chunk => {
str+=chunk;
});
//接受完客户端发送过来的数据触发
request.on('end',() => {
//完整的请求体数据
// console.log(str);
//使用querystring把字符串格式数据转化成对象形式
const body = qs.parse(str);
request.body=body;
// 不调用这个next下面接受不到值
next();
})
})
app.post('/',(requset,response) =>{
//设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
response.send(requset.body);
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.8.4封装自定义中间件
6.9使用express写接口
6.9.0POST请求头的常见数据格式
1、application/json(JSON数据格式)
xhr.setRequestHeader("Content-type","application/json; charset=utf-8");
这种类型是我们现在最常用的,越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
2、application/x-www-form-urlencoded
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据
3、multipart/form-data
xhr.setRequestHeader("Content-type", "multipart/form-data; charset=utf-8");
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 的 enctyped 等于这个值
4、text/xml
xhr.setRequestHeader("Content-type", "text/xml; charset=utf-8");
它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范,这种方式现在不常用
6.9.1GET接口
注意请求的url地址
添加查询字符串
6.9.2POST请求
发送携带urlencoded的请求 服务端不配置这个POST请求获取不到request.body的数据
app.use(express.urlencoded({extended:false}));
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>点击发送请求</button>
</body>
</html>
<script>
var btn = document.getElementsByTagName('button');
btn[0].addEventListener('click',function (){
var data = "name=houwang&age=18"
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:3000/api/post');
//发送urlencoded数据带的请求头
xhr.setRequestHeader("content-Type","application/x-www-form-urlencoded");
xhr.send(`${data}`);
xhr.onreadystatechange = function (){
//判断服务器响应了所有的结果
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
document.write(xhr.response);
}
}
}
})
</script>
router代码:
const express = require('express');
const router = express.Router();
router.post('/post',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
//通过request获取请求体中包含url-encoded格式的数据
const body = request.body;
response.send({
status:0, //状态码0成功,1失败
msg:'GET请求成功', // 提示信息
data:body //需要响应给客户端的数据
})
})
module.exports=router;
server代码:
const express = require('express');
const router =require('./06POST接口router');
const app = express();
//配置中间件来解析url-encoded格式的数据
app.use(express.urlencoded({extended:false}));
//app.use就是注册全局中间件 这里也可以把router认为是一个中间件
app.use('/api',router);
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.9.3跨域问题
之前我们解决跨域的问题是用这个
response.setHeader('Access-Control-Allow-Origin','*');
下载cors
npm i cors
导入后添加进去
app.use(cors());
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<button id="GET">GET</button>
<button id="POST">POST</button>
</body>
</html>
<script>
$(function (){
$('#GET').on('click',function (){
$.ajax({
type:'GET',
url:'http://127.0.0.1:3000/api/get',
data:{name:'houwang',age:18},
success:function (response){
console.log(response);
}
})
})
$('#POST').on('click',function (){
$.ajax({
type:'POST',
url:'http://127.0.0.1:3000/api/post',
data:{name:'houwang',age:18},
success:function (response){
console.log(response);
}
})
})
})
</script>
router代码:
const express = require('express');
const router = express.Router();
router.post('/post',(request,response) => {
/** response.setHeader('Access-Control-Allow-Origin','*');**/
//通过request获取请求体中包含url-encoded格式的数据
const body = request.body;
response.send({
status:0, //状态码0成功,1失败
msg:'POST请求成功', // 提示信息
data:body //需要响应给客户端的数据
})
})
router.get('/get',(request,response) => {
/** response.setHeader('Access-Control-Allow-Origin','*');**/
//通过request获取请求体中包含url-encoded格式的数据
const body = request.query;
response.send({
status:0, //状态码0成功,1失败
msg:'GET请求成功', // 提示信息
data:body //需要响应给客户端的数据
})
})
module.exports=router;
server代码:
const express = require('express');
const cors =require('cors');
const router =require('./01Jquery跨域问题router');
const app = express();
//配置中间件来解析url-encoded格式的数据
app.use(express.urlencoded({extended:false}));
//app.use就是注册全局中间件 这里也可以把router认为是一个中间件
app.use(cors());
app.use('/api',router);
app.listen(3000,function (){
console.log('服务器启动成功')
})
6.9.4CORS的介绍
6.9.5CORS的三个响应头
response.setHeader('Access-Control-Allow-Origin','*');
6.9.6简单请求和预检请求
基于6.9.3 前端添加代码:
$('#DELETE').on('click',function (){
$.ajax({
type:'DELETE',
url:'http://127.0.0.1:3000/api/delete',
data:{name:'houwang',age:18},
success:function (response){
console.log(response);
}
})
})
router添加如下代码
router.delete('/DELETE',(request,response) => {
// response.setHeader('Access-Control-Allow-Origin','*');
//通过request获取请求体中包含url-encoded格式的数据
const body = request.body;
response.send({
status:0, //状态码0成功,1失败
msg:'DELETE请求成功', // 提示信息
data:body //需要响应给客户端的数据
})
})
选中的这个就是预检请求
6.9.7JSONP接口
注:这个JSONP只支持GET请求
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<button>jsonp</button>
<div></div>
</body>
</html>
<script>
$(function (){
$('button').on('click',function (){
$.ajax({
method:"GET", //请求方式必须为GET
url:'http://127.0.0.1:3000/jsonp',
dataType:'jsonp', //表示发起JSONP请求
success:function (response){
document.write(response.name)
document.write(response.age);
}
})
})
})
</script>
后端代码:
const express = require('express');
const app = express();
//为了防止冲突必须在cors中间件前面声明JSONP接口
app.get('/jsonp',(request,response) => {
//获取请求头中函数名称callback
const funcName = request.query.callback;
//自定义一个返回的data数据
const data = {
name:'houwang',
age:18
}
//JSON.stringify()将对象转化为JSON格式字符串
response.send(`${funcName}(${JSON.stringify(data)})`)
})
app.listen(3000,function (){
console.log('服务器启动成功')
})
七、MySQL
7.1MySQL基本介绍
7.2增删查改
7.2.1 查询数据
– *号是通配符 查询users表所有列的数据
SELECT * FROM users
– 指定查询某一列
SELECT name FROM users
7.2.2插入数据
– 向users表插入三个列对应值为***
INSERT INTO users (id,name,pass) VALUES (1, '张三', '123')
INSERT INTO users (id,name,pass) VALUES (2, '李四', '456')
status是创建表设置好的默认值
7.2.3更新数据
UPDATE users SET pass=123456 -- 不加条件限制则更新所有数据
UPDATE users SET pass='admin123' WHERE id=1
7.2.4删除数据
注:删除数据一定要加where条件 不然就是删库跑路了
DELETE FROM users WHERE id=5 -- 删除id为5那行数据
7.3SQL子句
7.3.1WHERE子句
– 查询表中status大于1的数据
SELECT * FROM users WHERE `status`>1
– 查询表中status不等于1的数据
SELECT * FROM users WHERE `status`<>1
7.3.2and和or语句(并且或者)
– 查询表中pass等于admin123并且id小于4的数据
SELECT * FROM users WHERE pass='admin123' AND id<4
– 查询表中pass等于admin123或者id小于4的数据
SELECT * FROM users WHERE pass='admin123' or id<4
7.3.3ORDER BY子句(排序)
-- ASC升序(可不加) --DESC降序
SELECT * FROM houwang ORDER BY 分数 ASC
SELECT * FROM houwang ORDER BY 分数 DESC
先按照分数升序排序后再按学号降序排序
SELECT * FROM houwang ORDER BY 分数 ASC,学号 DESC
7.3.4COUNT函数(统计)
– 查询表中分数为65的总数据条数
select COUNT(*) FROM houwang WHERE 分数=65
7.3.5AS取别名
– 查询表中分数为65的总数据条数并且取别名
select COUNT(*) AS 分数和 FROM houwang WHERE 分数=65
– 查询表数据分数等于87,姓名取别名为name,分数取别名为num
SELECT 姓名 AS name,分数 AS num FROM houwang WHERE 分数=87
八、在项目中操作MySQL
8.1安装mysql
npm i mysql
8.2基本操作
8.3查询表
注:这里查询到的结果是一个数组
const mysql = require('mysql');
//建立与本地数据库的连接
const db = mysql.createPool({
host:'127.0.0.1', //数据库的ip地址
user:'root', //mysql登录用户名
password:'123456', //登录密码
database:'猴王' //你要操作的数据库
});
//整一条sql语句检测一下
db.query('select * from users',(err,data) => {
if(err) return console.log(err.message);
console.log(data)
})
8.4插入数据
const mysql = require('mysql');
//建立与本地数据库的连接
const db = mysql.createPool({
host:'127.0.0.1', //数据库的ip地址
user:'root', //mysql登录用户名
password:'123456', //登录密码
database:'猴王' //你要操作的数据库
});
//自定义一个我们想插入的数据
const data = {name:'王二麻子',pass:'123456'};
//自定义一个sql语句 ?表示占位符
const sqlstr = 'insert into users (name,pass) values (?,?)';
//通过数组的形式依次指定我们要插入的数据
db.query(sqlstr,[data.name,data.pass],(err,result) => {
if(err) return console.log(err.message);
//检测插入成功的一种方式 affectedRows:受影响
if(affectedRows=1) console.log('插入成功')
})
db.query('select * from users',(err,result) => {
if(err) return console.log(err.message);
console.log(result)
})
插入的快捷方式
const mysql = require('mysql');
//建立与本地数据库的连接
const db = mysql.createPool({
host:'127.0.0.1', //数据库的ip地址
user:'root', //mysql登录用户名
password:'123456', //登录密码
database:'猴王' //你要操作的数据库
});
/**快捷插入**/
//自定义一个我们想插入的数据
const data = {name:'王二麻子',pass:'123456'};
//?表示占位符 set ?就是一种简化的写法
const sqlstr = 'insert into users set ?';
//快捷插入不需要使用数组的形式
db.query(sqlstr,data,(err,result) => {
if(err) return console.log(err.message);
//检测插入成功的一种方式 affectedRows:受影响
if(result.affectedRows=1) console.log('插入成功')
})
db.query('select * from users',(err,result) => {
if(err) return console.log(err.message);
console.log(result)
})
8.5更新数据
核心代码:
const data = {id:5};
//?表示占位符 set ?就是一种简化的写法
const sqlstr = 'update users set id = ? where name="王二麻子"';
db.query(sqlstr,[data.id],(err,result) => {
if(err) return console.log(err.message);
if(result.affectedRows=1) console.log('更新成功')
})
快捷操作
核心代码:
/**更新快捷操作**/
const data = {id:6,name:'王二麻子'};
const sqlstr = 'update users set ? where name= ? ';
//与插入不同的是这里写的是一个数组
db.query(sqlstr,[data,data.name],(err,result) => {
if(err) return console.log(err.message);
if(result.affectedRows=1) console.log('更新成功')
})
8.6删除数据
实际中推荐使用id来进行删除
const sqlstr = "delete from users where id = ? "
//当sql语句只有一个占位符的时候数组可以不写
db.query(sqlstr,6,(err,result) => {
if(err) return console.log(err.message);
if(result.affectedRows=1) console.log('删除成功')
})
标记删除,你写不好sql你删错了你就知道rm -rf /*有什么作用了
// 标记删除
const sqlstr = "update users set status = ? where id =? "
//我们把status=2默认为删除了的数据
db.query(sqlstr,[2,4],(err,result) => {
if(err) return console.log(err.message);
if(result.affectedRows=1) console.log('标记删除成功')
})
九、web开发模式
9.1服务器端渲染和前后端分离渲染介绍
9.1.1服务器端渲染介绍
优缺点
9.1.2前后端分离渲染介绍
优缺点
9.1.3实际开发中如何选择
9.2身份认证
9.2.1session认证机制
network里请求头和Application里面都有cookies
9.2.2cookies的不安全性
9.3在express中使用session认证
9.3.1安装配置
安装
npm install express-session
//导入express-session
const session = require('express-session');
//配置 使用app.use中间件来进行挂载
app.use(session({
secret:'houwang', //任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
9.4登录小案例
9.4.1咱们前端发送数据过去,后端接受并返回回来
我们是POST请求必须带如下两个参数,request.body里面才有值
前端:
//发送urlencoded数据带的请求头 用来把字符串类型的参数序列化成Form Data
xhr.setRequestHeader("content-Type","application/x-www-form-urlencoded");
后端:
//配置中间件来解析url-encoded格式的数据
app.use(express.urlencoded({extended:false}));
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
账号:<input type="text" class="name"><br>
密码:<input type="text" class="password"><br>
<button>登录</button>
</body>
</html>
<script>
var btn = document.getElementsByTagName('button')[0];
btn.addEventListener('click',function (){
main();
})
function main(){
var name = document.getElementsByClassName('name')[0].value;
var password = document.getElementsByClassName('password')[0].value;
const data = `name=${name}&password=${password}`
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:3000/api/login');
//发送urlencoded数据带的请求头 用来把字符串类型的参数序列化成Form Data
xhr.setRequestHeader("content-Type","application/x-www-form-urlencoded");
xhr.send(data);
//设置响应体数据为JSON
xhr.responseType='json';
xhr.onreadystatechange = function (){
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
console.log(xhr.response.name);
console.log(xhr.response.password);
}
}
}
}
</script>
后端代码
const express =require('express');
const cors = require('cors');
const app = express();
//配置中间件来解析url-encoded格式的数据
app.use(express.urlencoded({extended:false}));
//解决跨域
app.use(cors());
//登录接口"
app.post('/api/login',(request,response) => {
const name = request.body.name;
const password = request.body.password;
const data = {
'name':name,
'password':password,
}
response.send(JSON.stringify(data));
})
app.listen(3000,() => {
console.log('服务器启动成功');
})
9.4.2后端设置固定的账号密码,前端并提示用户登录成功与失败
失败跳转注册页面,成功跳转百度
登录前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
账号:<input type="text" class="name"><br>
密码:<input type="text" class="password"><br>
<button>登录</button>
</body>
</html>
<script>
var btn = document.getElementsByTagName('button')[0];
btn.addEventListener('click',function (){
main();
})
function main(){
var name = document.getElementsByClassName('name')[0].value;
var password = document.getElementsByClassName('password')[0].value;
const data = `name=${name}&password=${password}`
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:3000/api/login');
//发送urlencoded数据带的请求头 用来把字符串类型的参数序列化成Form Data
xhr.setRequestHeader("content-Type","application/x-www-form-urlencoded");
xhr.send(data);
//设置响应体数据为JSON
xhr.responseType='json';
xhr.onreadystatechange = function (){
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
const status = xhr.response;
console.log(status);
if(status.LoginStatus==1){
alert(status.msg+'去注册');
window.location.href='http://www.baidu.com';
}else if(status.LoginStatus==0){
const trueorfalse = confirm(status.msg + '回车确认跳转')
if (trueorfalse) window.location.href='01index.html';
}
}
}
}
}
</script>
后端
const express =require('express');
const cors = require('cors');
const app = express();
//配置中间件来解析url-encoded格式的数据
app.use(express.urlencoded({extended:false}));
//解决跨域
app.use(cors());
//登录接口"
app.post('/api/login',(request,response) => {
const name = request.body.name;
const password = request.body.password;
if(name!=='admin'&&password!=='123456'){
return response.send({LoginStatus:1,msg:'登录失败'})
}else{
response.send({LoginStatus:0,msg:'登录成功'})
}
})
app.listen(3000,() => {
console.log('服务器启动成功');
})
9.4.3向seesion存入数据,并且首页检测是否登录
存储用户数据:
//user和isLogin是我们自定义的属性
request.session.user=request.body //存储用户的账号和密码信息
request.session.isLogin=true; //用户的登录状态
登录页面代码不变,post请求里面request.session.isLogin值在get请求访问不到,(因为跨域问题)我这里借助that来访问
首页前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
<script>
function main(){
const xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:3000/api');
xhr.send();
//设置响应体数据为JSON对象
xhr.responseType='json';
xhr.onreadystatechange = function (){
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
console.log(xhr.response)
document.write(xhr.response);
if(xhr.response.LoginStatus==1){
document.write('现在处于未登录状态'+xhr.response.msg)
}else{
document.write('你现在是登录成功状态'+xhr.response.msg)
}
}
}
}
}
main();
</script>
后端
const express =require('express');
const cors = require('cors');
//导入express-session
const session = require('express-session');
const app = express();
app.use(express.urlencoded({extended:false}));
app.use(cors());
//配置 使用app.use中间件来进行挂载
app.use(session({
secret:'houwang', //任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
var that = false;
app.post('/api/login',(request,response) => {
const name = request.body.name;
const password = request.body.password;
if(name!=='admin'&&password!=='123456'){
return response.send({LoginStatus:1,msg:'登录失败'})
}
//user和isLogin是我们自定义的属性
request.session.user=request.body //存储用户的账号和密码信息
request.session.isLogin=true; //用户的登录状态
that=request.session.isLogin;
console.log(request.session.user);
response.send({LoginStatus:0,msg:'登录成功'})
})
app.get('/api',(request,response) => {
if(that){
response.send({LoginStatus:0,msg:'success'})
}else{
return response.send({LoginStatus:1,msg:'fail'})
}
})
app.listen(3000,() => {
console.log('服务器启动成功');
})
9.4.4登出清除数据
上一个存入数据的逻辑,post请求里面request.session.isLogin值依旧访问不到,所以这个访问依旧没得用,这里我们借助info来实现,登录代码依旧不变
前端首页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
账号:<input type="text" class="name"><br>
密码:<input type="text" class="password"><br>
<button>登录</button>
</body>
</html>
<script>
var btn = document.getElementsByTagName('button')[0];
btn.addEventListener('click',function (){
main();
})
function main(){
var name = document.getElementsByClassName('name')[0].value;
var password = document.getElementsByClassName('password')[0].value;
const data = `name=${name}&password=${password}`
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:3000/api/login');
//发送urlencoded数据带的请求头 用来把字符串类型的参数序列化成Form Data
xhr.setRequestHeader("content-Type","application/x-www-form-urlencoded");
xhr.send(data);
//设置响应体数据为JSON
xhr.responseType='json';
xhr.onreadystatechange = function (){
if(xhr.readyState===4){
if (xhr.status>=200&&xhr.status<300){
const status = xhr.response;
console.log(status);
if(status.LoginStatus==1){
alert(status.msg+'去注册');
window.location.href='http://www.baidu.com';
}else{
const trueorfalse = confirm(status.msg + '回车确认跳转')
if (trueorfalse) window.location.href='01index.html';
}
}
}
}
}
</script>
后端
const express =require('express');
const cors = require('cors');
//导入express-session
const session = require('express-session');
const app = express();
app.use(express.urlencoded({extended:false}));
app.use(cors());
//配置 使用app.use中间件来进行挂载
app.use(session({
secret:'houwang', //任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
var that = false; //判断是否处在登录成功失败的状态
var info = true; //清空数据用的,清空了就是false
//登录接口记录值
app.post('/api/login',(request,response) => {
const name = request.body.name;
const password = request.body.password;
if(name!=='admin'&&password!=='123456'){
return response.send({LoginStatus:1,msg:'登录失败'})
}
//user和isLogin是我们自定义的属性
request.session.user=request.body //存储用户的账号和密码信息
request.session.isLogin=true; //用户的登录状态
that=request.session.isLogin;
console.log(request.session.user);
response.send({LoginStatus:0,msg:'登录成功'})
info=true;
})
//读取判断是否在登录状态
app.get('/api',(request,response) => {
if(info==false) that=false; //检测到数据被清空直接返回未登录状态
if(that){
response.send({LoginStatus:0,msg:'success'})
}else{
return response.send({LoginStatus:1,msg:'fail'})
}
})
//清空登录状态
app.post('/api/logout',(request,response) =>{
//清空session的方法 摆设而已压根访问不到request.session的值
request.session.destroy();
response.send({LoginStatus:1,msg:'out'})
info=false;
})
app.listen(3000,() => {
console.log('服务器启动成功');
})
9.5在express中使用jwt认证机制
9.5.1什么jwt机制
9.5.2安装配置
定义密钥
const secretKey = 'houwang' //用于JWT字符串的加密和解密
9.5.3jwt基操
后端代码
const express =require('express');
const cors = require('cors');
const jwt = require('jsonwebtoken') //生成jwt字符串
const expressJwt = require('express-jwt') //把JWT字符串还原成JSON格式对象
const app = express();
app.use(express.urlencoded({extended:false}));
app.use(cors());
const secretKey = 'houwang' //用于JWT字符串的加密和解密
//expressJwt({secret:secretKey}) 解析token的中间件 解析成JSON对象的中间件
//.unless({path:[/^\/api\//]}) 正则匹配的那些接口访问不需要权限
//注意,配置成功这个express-jwt这个中间件,就可以把解析出来的用户信息,挂载到request.user属性上
app.use(expressJwt({secret:secretKey}).unless({path:[/^\/api\//]}));
//登录接口记录值
app.post('/api/login',(request,response) => {
const name = request.body.name;
const password = request.body.password;
if(name!=='admin'||password!=='123456') {
return response.send({LoginStatus: 1, msg: '登录失败'})
}
//参数一:用户信息对象,参数二:加密的密钥,参数三:配置对象配置当前token的有效期
const tokenStr = jwt.sign({username: name}, secretKey, {expiresIn: '3h'});
response.send({LoginStatus:0,msg:'登录成功',token:tokenStr});
})
//正则表达式未包含这个域名 所以这是一个需要权限的接口
app.get('/admin',(request,response) => {
response.send({Status:200,data:request.user});
})
app.listen(3000,() => {
console.log('服务器启动成功');
})
下载安装外部软件postman
请求第一个登录接口
拿到第一次请求获得的token进行请求第二个需要权限的接口
请求方式两者不一,注意请求的值前面要加Bearer和一个空格
日常作怪模拟一下token出错,报一堆错
核心代码注:这个中间件放在最后面
//jwt捕获错误
app.use((err,request,response,next) => {
if(err.name==='UnauthorizedError') return response.send({Status:401,msg:'无效的token'})
response.send({Status:500,msg:'未知的错误'})
})
这不完美?