Http协议
- HTTP(Hyper Text Transfer Protocol)< 超文本传输协议> 的缩写. 是用于从WWW 服务器传输超文本到本地浏览器的传输协议.HTTP 是一个应用层协议, 由请求和响应构成, 是一个标准的个客户端和服务器模型。通常基于TCP连接方式
- 特点:
- 支持客户端/ 服务器模型
- 简单快速
- 灵活
- 无连接
- 无状态
- http与https的区别
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- 端口号不同http是80,https是443
- HTTP请求信息由3部分组成:
- 请求方法URI协议/版本
- 请求头(Request Header)
- 请求正文
- HTTP 响应由三部分组成:
- 状态行:
- 消息报头:响应报头后述
- 响应正文:服务器返回的资源内容
http模块原生启动服务
- 服务启动代码有三步,修改服务器代码一定要记得重启服务。
- 引入模块
const http=require(“http”); - 创建http模块实例
http.createServer()方法创建实例,次方法回调函数中有两个参数,分别是req–request对象的形参,res–response对象的形参
let server===http.createServer((req,res)=>{- 设置头部信息及编码格式,采用writeHead()
- 参数一,是状态码
- 参数二,是状态消息,不支持中文。
- 参数三,是头部信息,包括以什么样的资源来在浏览器上显示,以什么样的编码格式
res.write(“你是憨憨吗?”);//向浏览器端发送响应内容。
res.write(<script>window.onload(()=>{alert("对,你是憨憨!")}) </script>
);???
res.end("< h1 style=‘color:pink;’>我是憨憨!!</ h1>");//可以向浏览器发送响应内容,并结束响应的功能。
}); - 设置头部信息及编码格式,采用writeHead()
- 设置端口号
采用的方法listen()方法,- 参数一是端口号,不能用0,0000,9999,
- 参数二是设置服务器的IP地址,可以供局域网内的用户使用(可省),
- 参数三是回调函数,同打印使用,也可以省略。
server.listen(520,“192.168.19.149”,()=>{
console.log(“服务器已启动”);
})
- 引入模块
//1、引入
const http=require("http");
//2、创建实例
//req是request对象,请求对象,用来服务器端接受浏览器的值或参数的。
//res是response对象,响应对象,用来给浏览器传递服务器端的数据。
const sever=http.createServer((req,res)=>{
//text/html,MIME文件类型,多用途互联网邮件扩展类型,功能:告诉浏览器当前响应的资源类型
//状态消息不能使用中文,否则会报错
res.writeHead(200,"this is test",{"content-Type":"text/html;charset=utf8;"})//设置响应的头部信息,参数一状态码,参数二状态消息,参数三头部参数
res.write("我是write方法");//像浏览器端发送内容。
res.write("我是write方法2");
res.end(`<h1 style="color:red;">我是憨憨!</h1>`);//结束响应并将参数传递给浏览器并显示
//注:在写服务器端代码的时候,一定要在最后面调用结束响应的代码。如果没有结束响应浏览器会出现错误,而且占用资源。
});
//3、设置端口号
//参数一是用来设置端口号的
//参数二是用来设置访问IP的
sever.listen(1111,"10.10.32.162");
//本地自己访问可以使用localhost:端口号 127.0.0.1:端口号
Path模块的方法
const path = require("path");
//1、path.basename(path[, ext]),获取当前路径参数的最后一部分内容,分隔符是"/"
//path 路径(相对路径/绝对路径)
//ext 将获取的字符串从右向左依次匹配,匹配成功(完全匹配)截取掉参数字符串
console.log(path.basename("./a/b/c/d"));//d
console.log(path.basename(__filename, "法.js"));//02_Path模块的方
//2、 path.delimiter 获取当前系统的标识
//windows系统是;
//其它系统是 :
console.log(path.delimiter);//;
// 3、path.dirname(path),获取当前路径参数的上一级路径
console.log(__dirname);//C:\Users\Administrator\Desktop\三阶段\day06\11.2
console.log(path.dirname(__filename));//C:\Users\Administrator\Desktop\三阶段\day06\11.2
//4、path.extname(path),获取当前文件路径的后缀名,注:它是以“.”为分隔符
console.log(path.extname(__filename));//.js
//5、path.isAbsolute(path),判断当前路径参数是否是绝对路径。绝对路径返回true
console.log(path.isAbsolute(__filename));//true
//6、path.join([...paths]),将参数字符串拼接为路径,支持"./","../"
console.log(path.join(__dirname,"a","../ddd","../../cxcxxx"));//C:\Users\Administrator\Desktop\三阶段\day06\cxcxxx
//7、path.normalize(path),规范当前路径的格式,如果是"./",则会被省略
console.log(path.normalize("./a/b/c/d"));//a\b\c\d
//8、path.parse(path),以对象的形式分解当前路径。对象的内容包含,盘符、上一级路径、文件名(带后缀),文件名(不带后缀),文件的后缀名
console.log(path.parse(__filename));//{root: 'C:\\',dir: 'C:\\Users\\Administrator\\Desktop\\三阶段\\day06\\11.2',base: '02_Path模块的方法.js',ext: '.js',name: '02_Path模块的方法'}
URL模块
let str="http://www.baidu.com:8080/a/b/c/index.html?username=rypy&pwd=123456#href=1";
//将字符串类型的URL转换为URL对象
let url=new URL(str);
console.log(url.searchParams);//URLSearchParams { 'username' => 'rypy', 'pwd' => '123456' }类对象Map数据结构
console.log(url.searchParams.get("username"));//rypy
querystring模块
const querystring=require("querystring");
//1、querystring.parse(str[, sep[, eq[, options]]]);将URL类型的字符串参数转化为对象
//str URL类型的字符串
//sep 设置键值对之间的拆分符号,默认值是"&"
//eq 设置键名与键值的拆分符号,默认值是"="
//options
//decodeURIComponent 设置字串串中如果有中文,是否自动解码,默认自动解码
//maxKeys 最大字符串转码量 默认值是1000
let str=`username:rypy|pwd=123456|interest:吃饭,睡觉,打豆豆`;
console.log(querystring.parse(str,"|",":"));//[Object: null prototype] {username: 'rypy','pwd=123456': '',interest: '吃饭,睡觉,打豆豆'}
//2、querystring.stringify(obj[, sep[, eq[, options]]],将对象转化为URL格式的字符串
//obj 对象
//sep 设置键值对之间的添加符号,默认值是"&"
//eq 设置键名与键值的添加符号,默认值是"="
//options
// encodeURIComponent 设置字符串中如果有中文,是否自动编码,默认自动编码,编码格式是十六进制的数字
let obj= {username: 'rypy','pwd=123456': '',interest: '吃饭,睡觉,打豆豆'}
console.log(querystring.stringify(obj,"**"));//username=rypy**pwd%3D123456=**interest=%E5%90%83%E9%A5%AD%EF%BC%8C%E7%9D%A1%E8%A7%89%EF%BC%8C%E6%89%93%E8%B1%86%E8%B1%86
let str2=`username=rypy&pwd%3D123456=&interest=%E5%90%83%E9%A5%AD%EF%BC%8C%E7%9D%A1%E8%A7%89%EF%BC%8C%E6%89%93%E8%B1%86%E8%B1%86`;
console.log(querystring.parse(str2));//[Object: null prototype] {username: 'rypy','pwd=123456': '',interest: '吃饭,睡觉,打豆豆'}
//3、querystring.escape(str),将字符串中的中文编码为URL格式的字符串
let str="我是憨憨,哈哈!";
console.log(querystring.escape(str));//%E6%88%91%E6%98%AF%E6%86%A8%E6%86%A8%EF%BC%8C%E5%93%88%E5%93%88%EF%BC%81
//4、querystring.unescape(str),将URL格式的编码字符串进行解码操作。
let str=`%E6%88%91%E6%98%AF%E6%86%A8%E6%86%A8%EF%BC%8C%E5%93%88%E5%93%88%EF%BC%81`;
console.log(querystring.unescape(str));//我是憨憨,哈哈!
http响应状态码:
常见的MIME类型(通用型):
如何响应get请求
const http = require("http");
const querystring = require("querystring");
http.createServer((req, res) => {
//get请求获取参数,req.url
if (req.url !== "/favicon.ico") {//排除谷歌浏览器的自动小图标
let [path, param] = req.url.split("?");//采用split方法将地址栏的字符串,的路由和参数进行拆分
console.log(querystring.parse(param));//采用querystring模块的parse方法将字符串类型的数据转化为对象。
res.end();
}
}).listen(3333);
http原生部署静态资源
- css/login.css&userlist.css
- db/fileControl.js
const fs=require("fs");
const path=require("path");
function getHtmlText(filepath){
return new Promise((resolve,reject)=>{
fs.readFile(path.join(__dirname,"../",filepath),"utf8",(err,data)=>{
if(err)reject(err);
else resolve(data);
});
});
}
function getHtmlBuffer(filepath){
return new Promise((resolve,reject)=>{
fs.readFile(path.join(__dirname,"../",filepath),(err,data)=>{
if(err)reject(err);
else resolve(data);
});
});
}
module.exports={
getHtmlText,
getHtmlBuffer
};
- img/img.gif&video.mp4
- js/index.js
- index.html
- server.js
const http=require("http");
const {getHtmlText,getHtmlBuffer}=require("./db/fileControl");
http.createServer(async (req,res)=>{
//在互联网中一个URL对应一个资源
if(req.url!=="/favicon.ico"){
let [path,param]=req.url.split("?");
let htmlText="";
//注:路由一定要加"/"
switch(path){
case "/":htmlText=await getHtmlText("./index.html");break;
case "/index.js"://经过将JS文件部署为资源文件,那么整个服务器地址都可以访问静态资源
res.writeHead(200,"ok",{"content-Type":"text/JavaScript;charset=utf8;"});
htmlText=await getHtmlText("./js/index.js");
break;
case "/login.css":
res.writeHead(200,"ok",{"content-Type":"text/css;charset=utf8;"});
htmlText=await getHtmlText("./css/login.css");
break;
case "/img.gif":
res.writeHead(200,"ok",{"content-Type":"image/gif;charset=utf8;"});
htmlText=await getHtmlBuffer("./img/img.gif");
break;
case "/video.mp4":
res.writeHead(200,"ok",{"content-Type":"video/mp4;charset=utf8;"});
htmlText=await getHtmlBuffer("./img/video.mp4");
break;
}
res.end(htmlText);
}
}).listen(3333,"10.10.32.162")
post请求参数
- db/fileControl.js
- html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/login.css">//路由
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" id="username" name="username"><br>
密 码:<input type="password" id="pwd" name="pwd"><br>
<input type="submit" value="提交" class="but">
</form>
</body>
</html>
- json/user.json
- model/userModel.js
- server.js
const http=require("http");
const fileControl=require("./db/fileControl");
const querystring=require("querystring");
const userModel=require("./model/userModel");
const fc=new fileControl();
const umodel=new userModel();
http.createServer((req,res)=>{
if(req.url!=="/favicon.ico"){
//如果是post请求方法,采用事件监听的模式,获取参数并处理
let user={};
req.on("data",(data)=>{//监听post请求数据的事件,如果有数据则回调函数就可以拿到值。
user=querystring.parse(data.toString());
});
//注:如果采用了on来监听post事件,就必须监听end事件来结束响应。
req.on("end",async ()=>{//可以在此回调函数里面获取到data事件的值。
let htmlText="";
res.writeHead(200,"ok",{"content-Type":"text/html;charset=utf8"});
switch(req.url){
case "/":htmlText=await fc.getHtmlText("./html/index.html");break;
case "/login":
let flag=await umodel.getLoginByUSer(user);
if(flag){
htmlText="登录成功!";
}else{
htmlText="登录失败!";
}
break;
}
res.end(htmlText);
});
}
}).listen(3333);
MVC
- MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
- 模型(Model):进行数据管理和数据库设计;
- 控制器(Controller):负责转发请求,对请求进行处理;
- 视图(View):图形界面设计。
MVP
- MVP(Model–view–presenter ) 是MVC 设计模式派生出来的。MVP 经常用来创建用户界面。presenter 是作为一个“中间人”的角色存在。在MVP 中,所有页面显示逻辑都会被推送到presenter 处理。
- 模型(model):定义要在用户界面中显示或以其他方式操作的数据的接口。
- 视图(view):是一个被动界面,它显示数据并将用户命令(例如:表单事件)路由给presenter,以便对该数据进行操作。
- Presenter:对模型和视图进行操作。它从存储库(模型)中检索数据,并格式化数据以便在视图中显示。
- 注:此模式重用性会好一些
MVC,webMVC,MVVM
- MVC思想:M(模型)、V(视图)、C(控制)
- V(请求)->C(调用)->M(页面)->V
- webMVC(企业级):
- V(请求)->C(调用拿数据)->M(返回数据)->C(数据+页面)->V
- MVVM(前后端分离)
- M(模型(model文件夹、db文件夹):ES6、数据库)->
M(路由(Router文件夹):node)->
V(动态数据(view文件夹):vue+react)->axios+ajax(前后端:JSON数据)->
M(路由:node)->
M(模型:ES6) - V(静态页面(public文件夹:js,css,html):html+css+TS)
- M(模型(model文件夹、db文件夹):ES6、数据库)->
原生项目搭建
- 路径最好写相对的绝对路径,影响项目的可移植性
- 一个资源对应一个url(一个请求)
- userModel.js
const fs=require("fs");
const path=require("path");
class UserModel{
//1、根据文件名来获取文件内容
getTextByFilename(filename){
return new Promise((resolve,reject)=>{
fs.readFile(path.join(__dirname,"../",filename),"utf8",(err,data)=>{
if(err)reject(err);
else resolve(data);
});
});
}
//2、以JSON格式来获取文件的数据
getJSONbyFilename(filename){
return new Promise((resolve,reject)=>{
fs.readFile(path.join(__dirname,"../",filename),"utf8",(err,data)=>{
if(err)reject(err);
else resolve(JSON.parse(data));
});
});
}
//根据ID获取某个用户
async getUserByID(userid){
let userlist=await this.getUserlist();
return userlist.filter((item)=>{
if(item.userid*1===userid*1){
return true;
}
return false;
})[0];
}
//3、将新的内容写入JSON文件中
writeJsonByFilename(filename,data){
fs.writeFile(path.join(__dirname,"../",filename),JSON.stringify(data),(err)=>{
if(err)throw err;
});
}
//4、获取用户列表
async getUserlist(){
return await this.getJSONbyFilename("./db/json/user.json");
}
//根据用户名和密码判断登录
async login(user){
let userlist=await this.getUserlist();
for(let item of userlist){
if(item.username===user.username&&item.pwd===user.pwd){
return true;
}
}
return false;
}
//添加用户
async addUser(user){
let userlist=await this.getUserlist();
let newUser={
"userid":userlist[userlist.length-1].userid*1+1,
"username":user.username,
"pwd":user.pwd,
"isadmin":"0"
}
userlist.push(newUser);
try{
await this.writeJsonByFilename("./db/json/user.json",userlist);
return true;
}catch{
return false;
}
}
//删除用户
async delUser(userid){
let userlist=await this.getUserlist();
let newUserlist=userlist.filter((item)=>{
if(item.userid*1===userid*1){
return false;
}
return true;
})
try{
await this.writeJsonByFilename("./db/json/user.json",newUserlist);
return true;
}catch{
return false;
}
}
//修改用户
async updateUser(user){
let userlist=await this.getUserlist();
let newUserlist=userlist.filter((item)=>{
if(item.userid*1===user.userid*1){
item.username=this.isData(user.username)?item.username:user.username;
item.pwd=this.isData(user.pwd)?item.pwd:user.pwd;
item.isadmin=user.isadmin==="0"||user.isadmin==="1"?user.isadmin:item.isadmin;
}
return true;
})
try{
await this.writeJsonByFilename("./db/json/user.json",newUserlist);
return true;
}catch{
return false;
}
}
//判断用户修改数据的合法性
isData(params) {
return params===undefined||params===null||params==="undefined"||params==="null"||params===""?true:false;
}
}
module.exports=UserModel;
- html页面里面的路径改为路由
- login.html
<link rel="stylesheet" href="/login.css">
<div class="login">
<h1>用户登录</h1>
<h3>用户名:</h3><input type="text" name="username" id="username"><br>
<h3>密 码:</h3><input type="password" name="pwd" id="pwd"><br>
<button name="reg_btn" id="reg_btn" class="but">注册</button>
<button name="login_btn" id="login_btn" class="but">登录</button>
</div>
<script src="/jquery.min.js" type="text/javascript"></script>
<script src="/login.js" type="text/javascript"></script>
- userlist.html
<link rel="stylesheet" href="/userlist.css">
<table border="1">
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>密码</th>
<th>权限</th>
<th>操作</th>
</tr>
<tbody id="users">
</tbody>
</table>
<script src="/jquery.min.js" type="text/javascript"></script>
<script src="/userlist.js"></script>
- login.js
$("#login_btn").on("click",()=>{
let user={
username:$("#username").val(),
pwd:$("#pwd").val()
};
$.post("/loginon",user,(data)=>{
if(data.code===200){
location.href=data.page;
}else{
alert(data.msg);
}
});
});
$("#reg_btn").on("click",()=>{
let user={
username:$("#username").val(),
pwd:$("#pwd").val()
}
$.post("/reguestuser",user,(data)=>{
if(data.flag){
alert("注册成功!");
}else{
alert("注册失败!");
}
location.reload();
})
})
- userlist.js
{
$.post("/getUserlist",(data)=>{
drawTable(data);
});
}
function drawTable(list){
for(let item of list){
$("#users").append(`
<tr>
<td>${item.userid}</td>
<td>${item.username}</td>
<td>${item.pwd}</td>
<td>${item.isadmin==="1"?"管理员":"普通用户"}</td>
<td>
<button id="up_btn_view">修改</button>
<button id="del_btn">删除</button>
</td>
</tr>
`)
}
}
$("#users").on("click","#del_btn",(btnThis)=>{
let user={
userid:$(btnThis.target).parent().parent().children().eq(0).html()
}
$.post("/deluser",user,(data)=>{
location.reload();
});
});
$("#users").on("click","#up_btn_view",(btnThis)=>{
let upbtn=$(btnThis.target).parent().parent().children();
upbtn.eq(2).html(`<input type="text" id="pwd" name="pwd" value=${upbtn.eq(2).html()}>`);
if(upbtn.eq(3).html()==="管理员"){
upbtn.eq(3).html(`
<select id="isadmin">
<option value="1">管理员</option>
<option value="0">普通用户</option>
</select>
`);
}else{
upbtn.eq(3).html(`
<select id="isadmin">
<option value="0">普通用户</option>
<option value="1">管理员</option>
</select>
`);
}
upbtn.eq(4).html(`
<button id="up_btn">确认修改</button>
<button id="del_btn">删除</button>
`)
});
$("#users").on("click","#up_btn",(btnThis)=>{
let user={
userid:$(btnThis.target).parent().parent().children().eq(0).html(),
pwd:$("#pwd").val(),
isadmin:$("#isadmin").val()
}
$.post("/updateuser",user,(data)=>{
location.reload();
})
});
- server.js
const http=require("http");
const userModel=require("./model/userModel");
const querystring=require("querystring");
const umodel=new userModel();
http.createServer((req,res)=>{
let newData="";
if(req.url!=="/favicon.ico"){
req.on("data",(data)=>{
newData=querystring.parse(data.toString())
});
req.on("end",async()=>{
let htmlText="";
let resouceType="text/html";
switch(req.url){
case "/":htmlText=await umodel.getTextByFilename("./public/html/login.html");break;
case "/userlist.html":htmlText=await umodel.getTextByFilename("./public/html/userlist.html");break;
case "/login.css":
resouceType="text/css";
htmlText=await umodel.getTextByFilename("./public/css/login.css");
break;
case "/userlist.css":
resouceType="text/css";
htmlText=await umodel.getTextByFilename("./public/css/userlist.css");
break;
case "/jquery.min.js":
resouceType="text/JavaScript";
htmlText=await umodel.getTextByFilename("./public/js/jquery.min.js");
break;
case "/login.js":
resouceType="text/JavaScript";
htmlText=await umodel.getTextByFilename("./public/js/login.js");
break;
case "/userlist.js":
resouceType="text/JavaScript";
htmlText=await umodel.getTextByFilename("./public/js/userlist.js");
break;
case "/loginon":
resouceType="application/json";
if(await umodel.login(newData)){
htmlText=JSON.stringify({code:200,page:"/userlist.html"});//此处的page为字符串类型的路由
}else{
htmlText=JSON.stringify({code:201,msg:"登录失败!用户名或密码错误!"});
}
break;
case "/getUserlist":
resouceType="application/json";
htmlText=JSON.stringify(await umodel.getUserlist());
break;
case "/reguestuser":
resouceType="application/json";
htmlText=JSON.stringify({code:200,flag:await umodel.addUser(newData)});
break;
case "/deluser":
resouceType="application/json";
htmlText=JSON.stringify({code:200,flag:await umodel.delUser(newData.userid)});
break;
case "/updateuser":
resouceType="application/json";
htmlText=JSON.stringify({code:200,flag:await umodel.updateUser(newData)});
break;
}
res.writeHead(200,"ok",{"content-Type":`${resouceType};charset=utf8;`})
res.end(htmlText);
});
}
}).listen(5678);
express
- express简介
- Express 是一个简洁、灵活的node.js Web应用框架, 提供一系列强大特性API帮助使用者创建各种Web应用。从本质上说EXPRESS,完全由路由和中间件构成的web开发框架,即一个Express应用是在调用各种中间件。
- Express是一个基于node平台的web应用开发框架,帮助创建各种web应用。
- Express 的特性:路由 中间件
- express手动创建、express自动搭建
- express手动创建
- 新建文件夹,在当前目录下执行npm init或npm init –y创建一个package.json文件
- 命令行进入当前文件夹,安装Express插件:npm i express / npm i express -D
- 创建项目主文件app.js。
- 增加知识点:创建启动命令:在package.json里的"scripts"加上“start”:”nodeapp.js”,或者直接用npm app(文件名)
- express自动搭建:express-generator是Express生成器,可以通过该工具快速创建一个Express项目,其包含了很多搭建好的目录和配置文件和常规代码。
- 安装express-generator生成器:npm i express-generator -g
- 安装好后,通过express命令,创建项目:
express (参数) 项目名称
- 启动文件app.js
- 引入
const express=require(“express”); - 创建服务器实例
const app = express();
app.get("/",(req,res)=>{
res.send(“我是手动创建express路由!”);
}) - 设置端口号
app.listen(4444);
- 引入
- express手动创建
- express的路由
- get()方法响应get请求方式的路由:app.get("/",(req,res)=>{})
- 参数一是路由的名字,
- 参数二是回调函数
- 注:Express的路由响应规则是 方法名和请求方式必须一致,路由的名字必须一致
- res.send();//向浏览器发送数据(支持JSON格式数据),结束响应
- post()方法响应post请求方式的路由:app.post("/",(req,res)=>{})
- 参数一是路由的名字,
- 参数二是回调函数
- route(),当路由的名字相同,请求方式不同,并且需要的响应内容也不同的时候,可以采用route()方法的链式响应模式。
- app.route("/aaa").get((req,res)=>{}).post((req,res)=>{})
- app.put("/aa",(req,res)=>{
res.send(“我是put方法”);
}); - app.delete("/bbb",(req,res)=>{
res.send(“我是delete方法”);
}) - all()方法可以响应任何的同名路由
*
代表匹配所有路由,通常all方法与*
合用,用于实现拦截器功能。- app.all("*",(req,res)=>{});
- get()方法响应get请求方式的路由:app.get("/",(req,res)=>{})
手动创建express项目步骤
- app.js
//引入
const express=require("express");
const path=require("path");
//创建实例
const app=express();
//express中部署静态资源文件。采用的是一个中间件
//在express中使用中间件采用的方法 实例.use()
//express.static()中间件,参数一是一个绝对路径,此中间件会将路径下的所有文件部署为静态资源文件
app.use(express.static(path.join(__dirname,"public")))
//express的路由响应
//express响应get请求方式,采用 实例.get()
//get方法,参数一路由的名字,参数二回调函数,功能做响应的
app.get("/",(req,res)=>{
res.redirect("/html/index.html");//请求转发方法,重定向
//res.send("我是Express响应内容");//结束响应,并向浏览器发送显示内容。
});
//post请求方式,采用 实例.post方法
//post方法,参数一路由的名字,参数二回调函数,功能做响应的
app.post("/a",(req,res)=>{
res.send("好嗨哟!你们都蒙了!");
});
//链式路由方法 实例.route()
//route(),参数路由的名字
app.route("/bb").get((req,res)=>{
res.send("憨憨");
}).post((req,res)=>{
res.send("喜喜");
}).put((req,res)=>{
res.send("呵呵");
}).patch((req,res)=>{
res.send("哈哈哈");
})
//all方法,可以响应所有同名请求的路由
app.all("/aa",(req,res)=>{
res.send("我可以响应get,也可以响应post");
})
//*跟all(),结合可以匹配任意路由和任意请求。
//1、资源无法找到的时候可以友好的给用户返回消息
//2、登录拦截器
// app.all("*",(req,res)=>{
// res.send("我是*");
// });
//设置端口号
app.listen(1111);
- package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
},
优化项目:非原生(express、主路由、子路由、拦截器、校验码、分页![](https://i-blog.csdnimg.cn/blog_migrate/740d89d0e5ed9f3cc1118335a54f1991.png)
- model/usermodel.js
const fs = require("fs");
const path = require("path");
const svgCaptcha = require("svg-captcha")
const { isCheckData } = require("../until/until");
class UserModel {
//1、根据文件名来获取文件内容
getTextByFilename(filename) {
return new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname, "../", filename), "utf8", (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
//2、以JSON格式来获取文件的数据
getJSONbyFilename(filename) {
return new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname, "../", filename), "utf8", (err, data) => {
if (err) reject(err);
else resolve(JSON.parse(data));
});
});
}
//3、将新的内容写入JSON文件中
writeJsonByFilename(filename, data) {
fs.writeFile(path.join(__dirname, "../", filename), JSON.stringify(data), (err) => {
if (err) throw err;
});
}
//4、获取用户列表
async getUserlist() {
return await this.getJSONbyFilename("./db/json/user.json");
}
//根据用户名和密码判断登录
async login(user) {
if (isCheckData(user.username) || isCheckData(user.pwd)) {
return { code: 201, msg: "登录用户名或密码不合法!" };
} else {
let userlist = await this.getUserlist();
for (let item of userlist) {
if (item.username === user.username && item.pwd === user.pwd) {
return { code: 200, page: "/html/userlist.html" };
}
}
return { code: 201, msg: "登录失败!用户名或密码错误!" };
}
}
//添加用户
async addUser(user) {
if (isCheckData(user.username) || isCheckData(user.pwd)) {
return { code: 201, msg: "用户名或密码不合法!" };
} else {
let userlist = await this.getUserlist();
let newUser = {
"userid": userlist[userlist.length - 1].userid * 1 + 1,
"username": user.username,
"pwd": user.pwd,
"isadmin": "0"
}
userlist.push(newUser);
try {
await this.writeJsonByFilename("./db/json/user.json", userlist);
return { code: 200, msg: "注册成功!" };
} catch {
return { code: 201, msg: "注册失败!" };
}
}
}
//删除用户
async delUser(userid) {
let userlist = await this.getUserlist();
let newUserlist = userlist.filter((item) => {
if (item.userid * 1 === userid * 1) {
return false;
}
return true;
})
try {
await this.writeJsonByFilename("./db/json/user.json", newUserlist);
return true;
} catch {
return false;
}
}
//修改用户
async updateUser(user) {
let userlist = await this.getUserlist();
let newUserlist = userlist.filter((item) => {
if (item.userid * 1 === user.userid * 1) {
item.username = this.isData(user.username) ? item.username : user.username;
item.pwd = this.isData(user.pwd) ? item.pwd : user.pwd;
item.isadmin = user.isadmin === "0" || user.isadmin === "1" ? user.isadmin : item.isadmin;
}
return true;
})
try {
await this.writeJsonByFilename("./db/json/user.json", newUserlist);
return true;
} catch {
return false;
}
}
//获取校验码方法
getCodeNumber() {
return svgCaptcha.create({
width: 100,
height: 30,
fontSize: 25,
color: true
});
}
//校验码的封装
checkCode(sessionCaptcha, userCaptcha) {
if (isCheckData(userCaptcha)) {
return { flag: false, msg: "效验码不合法" }
} else {
if ((sessionCaptcha).toLowerCase() === (userCaptcha).toLowerCase()) {
return { flag: true, msg: "校验码正确!" };
} else {
return { flag: false, msg: "校验码输入有误!" };
}
}
}
//根据页码获取数据
async getUserlistByPage(page){
let userlist=await this.getUserlist();
let pageCount=5;//当前页显示的条数
let pageSum=Math.ceil(userlist.length/pageCount);//页码数
let newUserlist=[];
for(let i=0;i<pageCount;i++){
if(((page-1)*pageCount+i)<userlist.length){
newUserlist.push(userlist[(page-1)*pageCount+i]);
}
}
return {code:200,data:{userlist:newUserlist,pagelist:{pageCount,page,pageSum}}}
}
}
module.exports = UserModel;
- public/js/login.js
$("#login_btn").on("click",()=>{
let user={
username:$("#username").val(),
pwd:$("#pwd").val(),
jynumber:$("#jynumber").val()
};
$.post("/loginon",user,(data)=>{
if(data.code===200){
location.href=data.page;
}else{
alert(data.msg);
}
});
});
$("#reg_btn").on("click",()=>{
let user={
username:$("#username").val(),
pwd:$("#pwd").val(),
jynumber:$("#jynumber").val()
}
$.post("/reguestuser",user,(data)=>{
if(data.code===200){
alert(data.msg);
}else{
alert(data.msg);
}
location.reload();
})
});
$("#imgCode").on("click",()=>{
// location.href,跳转到某个页面/指定路由
// location.reload,//重新加载当前页面
//location.load()//加载指定路由/指定页面
$("#imgCode").load("/getCode");
});
- public/js/userlist.js
//预加载块,页面加载的时候自动加载
{
$.post("/user/getUserlist",{page:1},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
});
}
function drawTable(list){
$("#users").html("");
for(let item of list.userlist){
$("#users").append(`
<tr>
<td>${item.userid}</td>
<td>${item.username}</td>
<td>${item.pwd}</td>
<td>${item.isadmin==="1"?"管理员":"普通用户"}</td>
<td>
<button id="up_btn_view">修改</button>
<button id="del_btn">删除</button>
</td>
</tr>
`)
}
}
$("#users").on("click","#del_btn",(btnThis)=>{
let user={
userid:$(btnThis.target).parent().parent().children().eq(0).html()
}
$.post("/user/deluser",user,(data)=>{
location.reload();
});
});
$("#users").on("click","#up_btn_view",(btnThis)=>{
let upbtn=$(btnThis.target).parent().parent().children();
upbtn.eq(2).html(`<input type="text" id="pwd" name="pwd" value=${upbtn.eq(2).html()}>`);
if(upbtn.eq(3).html()==="管理员"){
upbtn.eq(3).html(`
<select id="isadmin">
<option value="1">管理员</option>
<option value="0">普通用户</option>
</select>
`);
}else{
upbtn.eq(3).html(`
<select id="isadmin">
<option value="0">普通用户</option>
<option value="1">管理员</option>
</select>
`);
}
upbtn.eq(4).html(`
<button id="up_btn">确认修改</button>
<button id="del_btn">删除</button>
`)
});
$("#users").on("click","#up_btn",(btnThis)=>{
let user={
userid:$(btnThis.target).parent().parent().children().eq(0).html(),
pwd:$("#pwd").val(),
isadmin:$("#isadmin").val()
}
$.post("/user/updateuser",user,(data)=>{
location.reload();
})
});
function divPageBtn(pagelist){
sessionStorage.setItem("page",pagelist.page);
sessionStorage.setItem("pageSum",pagelist.pageSum);
let htmlText=`
<button id="first_btn">首页</button>
<button id="up_btn">上一页</button>`
if(pagelist.page<5){
for(let i=1;i<=5;i++){
htmlText+=`<a href="javascript:;" id="page_a">${i}</a> `
}
htmlText+=`.......
<a href="javascript:;" id="page_a">${pagelist.pageSum}</a>
`
}else if(pagelist.page>=5&&pagelist.page<=(pagelist.pageSum-4)){
htmlText+=`
<a href="javascript:;" id="page_a">1</a>
.......
`
for(let i=-2;i<=2;i++){
htmlText+=`<a href="javascript:;" id="page_a">${pagelist.page*1+i}</a> `
}
htmlText+=`.......
<a href="javascript:;" id="page_a">${pagelist.pageSum}</a>
`
}else{
htmlText+=`
<a href="javascript:;" id="page_a">1</a>
.......
`
for(let i=-4;i<=0;i++){
htmlText+=`<a href="javascript:;" id="page_a">${pagelist.pageSum+i}</a> `
}
}
htmlText+=`<button id="next_btn">下一页</button>
<button id="end_btn">尾页</button>
`
$("#page_nav").html(htmlText)
}
$("#page_nav").on("click","#up_btn",()=>{
let page=sessionStorage.getItem("page")
if(page*1>1){
$.post("/user/getUserlist",{page:page*1-1},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
})
}
});
$("#page_nav").on("click","#next_btn",()=>{
let page=sessionStorage.getItem("page");
let pageSum=sessionStorage.getItem("pageSum");
if(page*1<pageSum){
$.post("/user/getUserlist",{page:(page*1+1)},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
})
}
});
$("#page_nav").on("click","#first_btn",()=>{
$.post("/user/getUserlist",{page:1},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
})
});
$("#page_nav").on("click","#end_btn",()=>{
let pageSum=sessionStorage.getItem("pageSum");
$.post("/user/getUserlist",{page:pageSum},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
})
});
$("#page_nav").on("click","#page_a",(btnThis)=>{
$.post("/user/getUserlist",{page:$(btnThis.target).html()},(data)=>{
if(data.code===200){
drawTable(data.data);
divPageBtn(data.data.pagelist);
}
});
});
- router/mainRouter.js
const express=require("express");
const userModel=require("../model/userModel");
const router=express.Router();
const umodel=new userModel();
//响应首页路由请求方法
router.get("/",(req,res)=>{
res.redirect("/html/login.html");
});
//响应“/loginon”路由
router.post("/loginon",async (req,res)=>{
let obj=umodel.checkCode(req.session.captchaText,req.body.jynumber);
if(obj.flag){//如果校验码验证成功,执行登录操作
let loginData=await umodel.login(req.body);
if(loginData.code===200)req.session.usersession={username:req.body.username};
res.send(loginData);
}else{//如果校验码失败,将信息返回
res.send({code:201,msg:obj.msg})
}
});
//响应“/reguestuser”路由
router.post("/reguestuser",async (req,res)=>{
let obj=umodel.checkCode(req.session.captchaText,req.body.jynumber);
if(obj.flag){
res.send(await umodel.addUser(req.body));
}else{
res.send({code:201,msg:obj.msg});
}
});
//响应校验码“/getCode”路由
router.get("/getCode",(req,res)=>{
let svgData=umodel.getCodeNumber();
req.session.captchaText=svgData.text;
res.type("svg");
res.send(svgData.data);
});
module.exports=router;
- router/userRouter.js
const express=require("express");
const userModel=require("../model/userModel");
const router=express.Router();
const umodel=new userModel();
//响应“/getUserlist”路由
router.post("/getUserlist",async(req,res)=>{
res.send(await umodel.getUserlist());
});
//响应“/deluser”路由
router.post("/deluser",async (req,res)=>{
res.send({code:200,flag:await umodel.delUser(req.body.userid)});
});
//响应“/updateuser”路由
router.post("/updateuser",async(req,res)=>{
res.send({code:200,flag:await umodel.updateUser(req.body)});
})
module.exports=router;
- until/until.js
//判断用户修改数据的合法性(不放进类中,可以只在需要的时候解构)
function isCheckData(params){
return params===undefined||params===null||params==="undefined"||params==="null"||params===""?true:false;
}
module.exports={
isCheckData
}
- app.js
const express=require("express");
const path=require("path");
const session=require("express-session");
const app=express();
//搭建post请求参数所需的中间件
app.use(express.urlencoded({extended:true}));
app.use(express.json());
//部署静态资源
app.use(express.static(path.join(__dirname,"public")));
//配置session参数
app.use(session({
secret:"userjson",
resave:true,
saveUninitialized:true
}));
//挂载子路由
//不需要拦截的路由
app.use("/",require("./router/mainRouter"));
//拦截器
app.use("*",(req,res,next)=>{
if(req.session.usersession!=="undefined"&&req.session.usersession!=={}&&req.session.usersession!==undefined){
next();
}else{
res.redirect("/html/login.html");
}
})
//需要拦截的静态资源
app.use(express.static(path.join(__dirname,"view")));
//需要拦截的路由
app.use("/user",require("./router/userRouter"));
//未找到对应路由处理中间件
app.use("*",(req,res,next)=>{
res.send("您访问的资源去了火星!404资源不存在!")
});
//服务器端代码错误处理中间件(错误中间件)
app.use((err,req,res,next)=>{
console.log(err.message);
res.send("服务挂掉了,管理员出差了,服务器错误500。");
})
app.listen(1234);
知识点
- 路由的名字:路由的形式大致可以分为三种:字符串,正则表达式,动态路由
- 字符串:"/aa"
- 正则表达式
?
匹配前面的单个字符0-1次:"/ab?cd"+
匹配前面的单个字符1-n次:"/ab+cd"*
可以以前面的字符为分割点,中间可以匹配任意字符:"/ab*cd".
只能作为分割点,且形式要匹配,通常跟*
结合,做同一类型匹配:"/*.css"
- 动态路由(冒号传参)
- 形式:变量的名字
- 注:冒号传参会默认增加一个层级的路由
- 优势在于,将会隐藏get请求参数的键名,直接传递值即可
- 冒号传参服务器端的获取方式req.params.变量名字
"/a/:username"
"/a/:username/:pwd"
.
连字符来设定冒号传参:"/a/:username.:pwd"
-
连字符来设定冒号传参:"/a/:username-:pwd"
- app.js下一般写主路由(只有一个主路由)
- router文件夹下可以写**多个子路由
- 主路由能完成的,子路由也能完成,相当于继承(不等于),模块化。
- 主路由与子路由以中间件进行嵌套使用。
- 子路由
- 引入express:const express = require(“express”);
- 创建子路由实例:const uRouter = express.Router();
- 抛出路由:module.exports = uRouter;
- 主路由(子路由配置为路由中间件)
- app.use(“/”,require(“./router/userRouter”));
子路由配置(细节)
- app.use(“/”,require(“./router/userRouter”));
- 子路由是通常根据功能来划分。
- 子路由在Express中获取get请求方式的参数,req.query.变量的名字,如果获取这个请求参数的对象req.query
- 子路由在Express中获取post请求方式的参数, 使用req.body.变量的名字,如果获取这个请求参数的对象req.body
- express中间件
- 中间件的定义:
- 中间件是可以访问请求对象(req),响应对象(res)以及next应用程序请求-响应周期中的函数。使用app.use() 来定义/使用中间件。
- 中间件执行顺序从上到下,从左到右。next()跳过当前代码块。
- use()方法有两个参数
- 参数一:可以再次配置路由(可以省略),
- 参数二是中间件
- 使用静态中间件static(),默认会将目录中的index.html作为首页。app.use("/aa",express.static(path.join(__dirname,“public”)));
- 中间件的定义:
- 中间件分为五大类:
- 应用级中间件:应用级中间件绑定到app对象,可以使用app.use,处理http请求的方法,例如GET、PUT、POST
- 路由级中间件:路由级中间件和应用级中间件类似,只不过是它绑定对象为express.Router()
- 内置中间件:
express.static()
负责在Express 应用中托管静态资源express.json()
解析req传入的json格式数据express.urlencoded()
解析req传入的urlencoded格式数据- app.use(express.urlencoded({extended:true}));
express.raw()
将请求有效内容解析为Bufferexpress.text()
将请求有效内容解析为字符串- Express中处理静态资源/静态资源服务器如何部署?
- 采用的是express.static():express.static(),它是Express的内置中间件,用来实现静态资源的部署,参数是静态资源的路径(尽量使用绝对路径)
- 自定义中间件:app.use()
- 自定义中间件,注:中间件不区分请求方式。只有路由的名字匹配即可。
- 回调含两个参数的中间件:app.use("/aa",(
req,res
)=>{}); - 三个参数的中间件:app.use("/bb",(
req,res,next
)=>{next(“sssssss”);//next()移交请求控制权,将当前请求移交给下一响应
}); - 四个参数:app.use("/bb",(
args,req,res,next
)=>{args用来接受next方法传递的参数
})
- 回调含两个参数的中间件:app.use("/aa",(
- 常用的第三方中间件有:
cookie-parser
:解析cookie- ejs:ejs模板
express-session
:解析session- http-errors:错误中间件处理
- serve-favicon:标题栏上小图标设置
nodemon
:热更新svg-captcha
:图形验证码插件formidable
:表单解析插件
- 自定义中间件,注:中间件不区分请求方式。只有路由的名字匹配即可。
- 错误中间件:错误处理中间件一般写在index.js或app.js最后,即当所有路由都无法匹配的时候进行处理。
- 错误中间件一定是4个参数
- 采用自定义中间件来定义页面不存在错误404
- 错误中间件:app.use((
err,req,res,next
)=>{console.log(err);res.send(“服务器产生了内部矛盾,管理员正在紧张处理中!”);});
- 错误中间件一定是4个参数
- 注:中间件的执行是自顶向下顺序执行的,所以一定要注意中间件的调用次序:且错误中间件的next不需要调用。
- cookie、session
- H5中处理跳转的两个属性:
- location.href(前端)
- res.redirect(后端)
- H5中新出的处理cookie本地存储的两个属性:
- local.storage 本地存储
- session.storage 浏览器会话存储
- 前端解决跨域问题:ajax中的jsonp
- cookie与session的区别:
- cookie是本地存储,单个文件最多4KB,超出4KB的多个文件,cookie数据存放在客户的浏览器上。
- session是会话存储,可以设定有效时间,安全性高于cookie(随着浏览器打开打开,关闭而关闭),session放在服务器上。
- localstorage与cookie
- 都是本地的,一个是浏览器本地存储,一个是服务器的本地存储
- sessionstorage与session
- 都是会话级别的,一个是浏览器会话存储,一个是服务器的会话存储
- H5中处理跳转的两个属性:
- Express的第三方中间件(5个重要)
nodemon热更新
重要(npm i nodemon -g )- 作用:代替命令行运行,自动重启。
cookie-parser
重要(npm i cookie-parser )- 作用:负责设置客户端的cookie相关信息,或者获取浏览器端相关的cookie信息。用于服务器端,路由与路由之间参数的传递。
- 如果cookie不设置默认值,则其结束的时间是当前会话。
- res.cookie()方法,用来设置存储cookie。
- 参数一是键名,
- 参数二是键值,
- 参数三是设置当前cookie键值对的相关信息{"expires”:设置过期时间有效期 }
- req.cookies,获取cookie { }
express-session
重要(npm i express-session)- 作用:设置浏览器会话的相关信息。
- app.use(session({
secret:“textSession”,设置session的ID
resave:true,如果同名session存在,将会被覆盖,默认值是false
saveUninitialized:true,设置是否保存未初始化的session,默认值是false
})); - req.session.username=“rypy”;存储session
- req.session.username,获取session
svg-captcha
重要(npm i svg-captcha)- 作用:把text内容转成svg图形返回使用
- create()方法,创建校验码图片对象,可以用对象的形式配置参数
- 参数:
noise
:4,设置干扰线的条数,默认值是1条。
fontSize
:40,设置字体大小
width
:80,设置图片的宽度
height
:50,设置图片的高度
color
:false,设置字体的颜色,其值是布尔类型,
background
:“pink”,设置图片的背景色。如果设置了图片的背景色,默认会开启字体颜色,且color会失效
ignoreChars
:"abcd"设置排除字符,此属性,是让验证码不会出现哪些字符,注:它区分大小写
- 参数:
- svg.text,以字符串的形式获取当前校验码的图片内容
- res.type(“svg”);设置响应数据的类型
- svg.data,以流的形式获取当前校验码图片
Formidable
重要(npm i formidable)- 作用:解析表单数据,和文件上传
- 如果需要使用formidable()插件,需要在前端HTML页面中配置”enctype=“multipart/form-data”,否则是无法获取提交的数据。< form>标签中设置。
- 创建一个formidable实例:let formText=formidable({multiples:true});
- ==formText.keepExtensions=true;==设置上传文件时的临时缓存文件带后缀名。
- formText.maxFileSize=200 * 1024 * 1024*1024;设置上传文件的大小。默认值是200mb
- 使用formidable实例的parse方法的回调函数来获取对应的值。注:parse()的参数一,一定是req与路由相同的请求对象。
formText.parse(req,(err, fields, files)
=>{管道流:读流.pipe(写流)});- err,错误信息
- fields,除文件以外所有的参数,且以对象的形式返回
- files,文件的流参数。
- http-errors(npm i http-errors)
- 作用:创建HTTP错误,返回HTTP状态的一个第三方中间件函数。使用next方法可以将错误返回给浏览器,并显示。
- next(errorhttp(404,“this is http error 憨憨”));
- serve-favicon(npm i serve-favicon)
- 作用:设置浏览器标签标题栏上的小图标。
- app.use(favion(path.join(__dirname,“img”,“timg.gif”)));
express中获取get请求参数
在Express中使用req.query获取get请求的参数,参数是一个JSON格式的对象
const express=require("express");
const app=express();
app.get("/",(req,res)=>{
res.send(req.query);
});
app.listen(1111);
express中获取post请求参数
req.body,当配置了处理post请求参数的中间件以后,可以使用req.body获取post请求的参数。
const express=require("express");
const app=express();
//post请求由于是经过编码,格式的数据,所以需要采用Express的两个中间件进行操作
//express.urlencoded()//express中的解码中间件
//express中如果使用中间件,采用app.use方法,功能将中间件挂载到当前服务器
//extended:true,指继承了body-parse中间件
app.use(express.urlencoded({extended:true}));
//express.json()中间件,设置整个服务器传递参数的格式是JSON格式的数据
app.use(express.json());
app.post("/aa",(req,res)=>{
res.send(req.body);
});
app.listen(1111);
路由的形成
const express=require("express");
const app=express();
//1、字符串类型的路由
app.get("/abcd",(req,res)=>{
res.send("我是abcd路由");
});
app.get("/aaa",(req,res)=>{
res.send("我是aaa");
});
//2、正则表达式 ?
//?,匹配?前面的单个字符0~1次
app.get("/ab?cd",(req,res)=>{//acd,abcd
res.send("我是?路由!");
});
//+,匹配+前面的单个字符1~n次
app.get("/ab+cd",(req,res)=>{
res.send("我是+路由!");
});
//*,通配符,只要以*前面的字符开头,* 后面字符结尾都可以匹配
app.get("/ab*cd",(req,res)=>{
res.send("我是*路由!");
});
//. 书写的通常被用作资源文件的后缀匹配,需要完全匹配
app.get("/ab.cd",(req,res)=>{
res.send("我是.路由!");
});
//动态路由(冒号传递参数)
//注:动态路由传递的参数每增加一个/,相当于增加了一个层级的路由
//形式一
app.get("/aa/:username/:pwd",(req,res)=>{
res.send("我是动态路由"+req.params.username);
});
//形式二,-连接符
//http://localhost:1111/aa/hahha-123-bb
app.get("/aa/:username-:pwd-:sex",(req,res)=>{
res.send("我是动态路由"+JSON.stringify(req.params));
//我是动态路由{"username":"hahha","pwd":"123","sex":"bb"}
});
//形式三,.连接符
app.get("/a.b/:username.:pwd.:sex",(req,res)=>{
res.send("我是动态路由"+JSON.stringify(req.params));
});
//混合使用
app.get("/aa/:username/bb/:pwd-:sex.:inter/cc/d*d",(req,res)=>{
res.send("我是动态路由"+JSON.stringify(req.params));
});
app.listen(1111);
子路由的形成
- router/mainRouter.js
//子路由文件:功能:将所有的路由响应,根据不同的功能,分成不同的子路由
const express=require("express");
const router=express.Router();//创建子路由实例
//响应get的"/"路由
router.get("/",(req,res)=>{http://localhost:1108/a/
res.send("我是子路由中的响应");
})
//响应get的"/aa"路由
router.get("/aa",(req,res)=>{//http://localhost:1111/a/aa/
res.send("我是子路由中的aa路由");
})
module.exports=router;
- app.js
const express=require("express");
const app=express();
//使用use方法挂载中间件,是有两个参数,参数一是路由的名字(注:如果此处增加了一个层级的路由,相当于接口增加了一个层级的路由),参数二是子路由的引入路径
app.use("/a",require("./router/mainRouter"));
app.listen(1111);
自定义中间件
const express=require("express");
const app=express();
app.get("/",(req,res)=>{
res.send("我是主路由");
});
app.get("/a",(req,res)=>{
res.send("我是a路由");
});
//带路由的自定义中间件,
//注:带自定义的路由,必须写在不带路由的前面。
app.use("/user",(req,res)=>{
res.send("你所访问的路由为user,但是它去了木星!");
});
//自定义中间件
//1、处理未响应的路由,会将无法响应的路由全部捕获,并根据代码返回内容
//2、如果采用自定义中间件响应,未处理路由,必须将响应路由的最后面,端口号的前面。
//3、自定义路由不区分get请求和post请求
//4、如果自定义中间件中,回调函数的参数有next方法,则,可以使用next方法移交响应控制权,给后面的路由,且其本身方法可以传递参数给下一个路由,但是下一个路由的回调函数必须是四个参数,第一个是接受此值的参数,参数二是请求对象,参数三是响应对象,参数四:next方法,四个参数缺一不可。
app.use((req,res,next)=>{
if(req.url==="/type"){
next("我是一个传值的");//Express的next,可以将响应控制权移交。
}else{
res.send("你访问的路由去了火星哟!!!");
}
})
app.use("/type",(args,req,res,next)=>{
// console.log(args);//我是一个传值的
res.send("我是全捕获后面的type"+args);
})
app.listen(1111);
错误中间件
const express=require("express");
const app=express();
app.get("/",(req,res)=>{
// console.log(age);
// let age=20; //服务器端代码出现错误,管理员休假了!
throw new Error("哈哈");//管理员去了水星,去找包同学!
res.send("我是首页路由!");
});
//定义错误中间件,功能:用来捕获服务器端代码出现问题的时候,做友好处理的中间件
//错误中间件回调函数必须有四个参数,第一个参数是错误信息,第二参数是请求对象,第三个参数是响应对象 ,第四个参数移交控制权。
app.use((err,req,res,next)=>{
if(err.message.includes("before initialization")){
//next();
res.send("服务器端代码出现错误,管理员休假了!");
}else{
res.send("管理员去了水星,去找包同学!");
}
});
app.listen(1111);
cookie-parser中间件的使用
const express=require("express");
const cookieParser=require("cookie-parser");//引入插件
const app=express();
app.use(cookieParser());//应用为中间件
app.get("/",(req,res)=>{http://10.10.32.162:1111/
//参数一,cookie的键名
//参数二,cookie的值
//参数三,cookie的选项
//设置coookie,信息及参数,
res.cookie("username","this is bug ctrols all things fall restart computer",{
expires:new Date(new Date().getTime()+1000*60*60*24*365*10000)//设置cookie的有效期,值必须是时间戳
})//向客户端写入cookie,
res.send("我是存cookie");
});
app.get("/getcookiedcode",(req,res)=>{//http://10.10.32.162:1111/getcookiedcode
//req.cookies未签名
res.send(req.cookies.username);//this is bug ctrols all things fall restart computer
})
app.listen(1111,"10.10.32.162");
express-session中间件的使用
const express=require("express");
const session=require("express-session");
const app=express();
app.use(session({//将session中间件与服务器进行绑定,并设置响应的参数。
secret:"test",//设置当前sessionID
saveUninitialized:true,//设置当session未初始化的时候也会保存
resave:true,//设置如果出现同名session的时候的覆盖保存
// cookie:{
// //expires://设置有效期
// maxAge:60000//设置有效期,直接是数字即可,单位是ms
// }
}));
app.get("/",(req,res)=>{//http://localhost:1111/
req.session.aaaa="mingzi";
res.send("我是存储session的方法");
});
app.get("/getSession",(req,res)=>{http://localhost:1111/getSession
res.send("我是取session"+req.session);
});
app.get("/getaaaa",(req,res)=>{http://localhost:1111/getaaaa
res.send("我是取"+req.session.aaaa);
});
app.listen(1111);
EJS模板引擎(npm i ejs )
- EJS 是一套简单的模板语言,更加友好的拼接字符串
- EJS用法:
- EJS 完全支持HTML标签
- js代码书写标签,不需要写script标签,哪里用哪里嵌套。
<% %>
- 输出HTML转义标签 ,当输出的变量的值中,如果含有HTML标签则会被转义
<%=username%>
- 在EJS中式关键标签,如果需要输出
<%% %%>
- EJS 采用双标签模式,有开始必须得有结束
- EJS常用语法:
<%xxxxxx%>
xxxx可以替换为任意的js,不会输出任何内容给浏览器<%-xxxxxx%>
xxxx可以替换为任意的js变量,渲染标签<%=xxxxxx%>
xxxx可以替换为任意的js变量,不渲染标签<%#xxxxxx%>
xxxx在生成HTML页面的时候会被忽略(注释)
- index.ejs
<body>
<%-include("head.ejs") %>
<br>
<!-- EJS 页面语法完全支持HTML,js,css语法 -->
我是EJS引擎页面
<%# 我是一个EJS注释,EJS的注释在转义为HTML的时候不会被转义 %>
<%# EJS 所有的标签都是以 小于号 百分号 开始,和结束 %>
<%# 下面是EJS 中书写js 代码 %>
<%
let age=20;
//console.log(age);//由于EJS是服务器端模板引擎,所以打印会在终端输出。
%>
<%#
下面是EJS 中 输出js代码中的变量
等号 放在标签中可以直接输出服务器端传递回来的对象的值
如果值中含有HTML标签不会被转义
中- 可以输出并将HTML标签转义
EJS的标签是双标签,且严格模式,任何地方出现问题都会报错,且是前后端同时报错。
%>
<%=age%>
<%=username%>
<%-username%>
<%=msg %>
<%-msg %>
<%=user.usernmae%>
<table border="1">
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>密码</th>
<th>权限</th>
</tr>
<%
for(let item of userlist){
%>
<tr>
<td><%=item.userid%></td>
<td><%=item.username%></td>
<td><%=item.pwd%></td>
<td><%=item.isadmin==="0"?"普通用户":"管理员"%></td>
</tr>
<%
}
%>
</table>
<%% 将百分号和大于号输出在页面 %%>
</body>
- app.js
const express=require("express");
const ejs=require("ejs");
const path=require("path");
const app=express();
//将ejs模板引擎加入到服务器中
//app.set()设置服务器参数,
//设置页面文件后缀的格式
app.engine("js",ejs.renderFile);
//view engine,是设置模板引擎页面参数,默认模板引擎参数的路径是views
app.set("view engine","js");
//设置模板引擎的页面路径
app.set("views",path.join(__dirname,"aa"));
//模板引擎的原理,通过服务器端将模板引擎语法转义为HTML语法,通常会跟静态页面相结合转义。
app.get("/",(req,res)=>{
res.render("index",{username:"rypy",msg:`<h1 style="color:red;">我是飘红</h1>`,user:{usernmae:"rypy1"},userlist:[{"userid":4,"username":"rypy87","pwd":"123456","isadmin":"1"},{"userid":5,"username":"admin6","pwd":"12334","isadmin":"1"},{"userid":6,"username":"admin7","pwd":"123456","isadmin":"1"},{"userid":7,"username":"admin8","pwd":"123456","isadmin":"0"},{"userid":8,"username":"admin9","pwd":"123","isadmin":"0"},{"userid":12,"username":"admin","pwd":"123456","isadmin":"1"},{"userid":14,"username":"rypy","pwd":"123456","isadmin":"1"},{"userid":22,"username":"admin10","pwd":"123456","isadmin":"1"},{"userid":23,"username":"admin111","pwd":"123","isadmin":"0"}]});//功能:渲染模板引擎的方法,向EJS页面传递参数
//res.redirect("/views/index.ejs");//功能请求转发
//res.send("dfdsfsdfds");//功能将字符串或JSON格式的数据传递给浏览器,结束响应。
})
app.listen(1111);
svg-captcha插件的使用
const svg=require("svg-captcha");
function getCode(){
let svgData=svg.create({//创建校验码实例
noise:4,//设置干扰线的条数,默认1条
fontSize:50,//设置字符的大小
size:6,//设置字符的个数,默认值4
width:300,//设置校验码图片的宽度
height:150,//设置校验码图片的高度
//color:true//设置字符是否含有颜色,取值只能是布尔值
background:"pink",//设置背景色,自带将字符随机颜色。
ignoreChars:"ABCM123456789"//设置排除字符,可用正则
});
console.log(svgData);//{text:"值",data:"数据流"}
return svgData;
}
function getCode(){
let svgData=svg.createMathExpr({
mathMin:0,//设置运算的最小值,默认值是1
mathMax:9,//设置运算的最大值,默认值是9
mathOperator:"+-"//设置运算的加减操作,
});
console.log(svgData.text);
return svgData;
}
module.exports={getCode};
const express=require("express");
const {getCode}=require("./model/mm");
const path=require("path");
const app=express();
//当把某一个文件夹内的页面设置为静态资源文件夹,其主路径下的index.html默认会挂载到"/"主路由下
app.use(express.static(path.join(__dirname,"public")));
app.get("/getCode",(req,res)=>{
let svgdata=getCode();
// req.session.captcha=svgdata.text;
res.type("svg");//设置返回资源的格式,采用type()方法。(可以保存.svg图片)
res.send(svgdata.data);
});
app.listen(1111);
formidable插件的使用
const express=require("express");
const path=require("path");
const fs=require("fs");
const formbale=require("formidable");
const app=express();
app.use(express.static(path.join(__dirname,"public")));
app.post("/getformdata",(req,res)=>{
const formData=formbale({multiples:true});//创建了一个formidable实例,使用multiples属性设置接受form表单提交的全部数据。
//使用实例的parse方法来接受表单数据,注:参数一必须要跟路由的回调请求对象保持一致,参数二是回调函数,用来获取表单数据的。
formData.keepExtensions=true;//设置当前form插件的文件读取含有后缀名。
formData.maxFileSize=200 * 1024 * 1024*1024;//设置上传文件的大小。默认值是200mb
formData.parse(req,(err, fields, files)=>{
//fields,接受表单提交的所有文本内容
//console.log(fields);
//files,如果表单中存在数据流类型的文件,files会以对象的形式返回这个文件对象
const rstream=fs.createReadStream(files.fineimg.path);
const wstream=fs.createWriteStream(path.join(__dirname,"img",files.fineimg.name));
rstream.pipe(wstream);
// console.log(files.fineimg.name);
});
res.send("表单成功!")
});
app.get("/getvideo",(req,res)=>{
res.type("mp4");
res.send(fs.readFileSync(path.join(__dirname,"img","你的答案.mp4")));
});
app.listen(1111,"10.10.32.162");
http-errors插件的使用
const express=require("express");
const errss=require("http-errors");
const app=express();
//使用中间件的模式使用http-errors插件
//必须采用next方法抛出自定义错误,这样才可以在页面中显示,
//插件的构造方法中,参数一 是错误代码,参数二是错误消息。
app.use("/err",(req,res,next)=>{
next(errss(401,"我是自定义错误!"));
});
app.listen(1111);
serve-favicon插件的使用
const express=require("express");
const favicon=require("serve-favicon");
const path=require("path");
const app=express();
//小图标插件需要以中间件的形式引入
//插件的构造方法中,需要传递绝对路径
app.use(favicon(path.join(__dirname,"img/img.gif")));
app.get("/",(req,res)=>{
res.send("我是设置小图标!");
})
app.listen(1111);
给a标签禁止提交,赋予按钮响应href="javascript:;"
获取另外一个事件的数据:前端用sessionstorage.setItem/getItem
mysql
数据库管理系统(DBMS)
- 对数据进行管理的软件,是数据库系统的核心组成部分。
- 主要功能:数据定义、数据操纵、数据库运行管理、数据库的建立和维护功能、数据通信。
- 常见的数据模型有:网状、关系、层次、面向对象。
Oracle
:Oracle Database,又称Oracle RDBMS、Oracle。是甲骨文公司的一款关系数据库管理系统。- 特性:处理速度快,非常快,安全级别高,支持快闪以及完美的恢复(即硬件坏了,也可以恢复到故障发前一秒),可以做到30s内故障转移,网格控制,和数据仓库方面也非常强大。
MySQL
:MySQL是一个小型关系型数据库管理系统,被广泛地应用在中小型企业级应用。也是甲骨文公司的一款产品。- 特性:开放源码、高度非过程化、面向集合的操作方式、以一种语法结构提供多种使用方式。
MS SQLServer
:SQLserver数据库是微软公司发布的一款RMDBS数据库,也就是关系型数据库系统。- 特性:图形化用户界面、客户服务器体系结构、丰富的编程接口工具、与Windows NT完成集成、具有很好的伸缩性。
DB2
:DB2是一系列混合数据管理产品,提供完整的AI 支持的能力,旨在帮助您管理本地、私有云和公共云环境中的结构化和非结构化数据。目前隶属于IBM公司- 特性:无限的扩展能力、应用透明、持续的高可用性。
常见的数据库模型
- 关系型数据库:(SQL)
- 常见的关系型数据库有MySQL,Oracle,SqlServer等。
- 关系型数据库大多数都遵循SQL(结构化查询语言,StruturedQuery Language)标准。
- 常见的操作有查询,新增,更新,删除等。注:针对结构化数据,使用关系型数据库更好。
- 非关系型数据库:(NoSQL,Not Only SQL)
- 非关系型数据库(NoSQL)NoSQL数据存储不需要固定的表结构,目前也没有统一的标准,不存在连接操作。相对于关系型数据库大数据存取上具有明显的性能优势。
- 大致分两类:
- 1、搜索键值存储数据库(key-value)此类数据库类似传统语言中的哈希表。通过键名key来添加、查询或者删除数据库。性能和扩展性很好、操作简单、易部署、高并发。典型产品:Memcached、Redis。
- 2、面向文档(Document-Oriented)数据库键值数据库的升级版,将数据以文档形式存储,增加了嵌套键值功能,查询效率更高。典型产品:MongoDB。
- 关系和非关系型数据库的区别
- 存储方式
- 关系型数据库采用表格的储存方式,数据以行和列的方式进行存储。非关系型数据库以数据集的方式,大量的数据集中存储在一起,类似于键值对、图结构或者文档。
- 存储结构
- 关系型数据库按照结构化的方法存储数据,需要先定义好表的结构,根据表的结构进行数据存储。非关系型数据库采用的是动态结构,可根据数据存储的需要灵活的改变数据库的结构。
- 存储规范
- 关系型数据库为了避免重复、规范化数据以及充分利用好存储空间,把数据按照最小关系表的形式进行存储。非关系型数据库的数据存储方式是用平面数据集的方式集中存放。
- 扩展方式
- 关系型数据库只具备纵向扩展能力。非关系型数据库还可以采用横向的方式来扩展。
- 查询方式
- 关系型数据库采用结构化查询语言(Structured Query Language,即SQL)来对数据库进行查询。非关系型数据库采用的数据访问模式查询。
- 事务性
- 关系型数据库强调ACID规则(
原子性
(Atomicity)、一致性
(Consistency)、隔离性
(Isolation)、持久性
(Durability))。非关系型数据库强调BASE原则(基本可用
(Basically Available)、软状态
(Soft-state)、最终一致性
(Eventual Consistency))。
- 关系型数据库强调ACID规则(
- 授权方式
- 关系型数据库,常见的有Oracle,SQLServer,DB2,Mysql等。非关系型数据库,常见的有redis,HBase,MongoDb,memcache等产品,通常都采用开源的方式。
- 存储方式
范式
- 范式是对关系的不同数据依赖程度的要求。即通过模式分解将一个低级范式转换为若干个高级范式的过程称作规范化。
- 目前关系数据库有六种范式:
第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)
。 - 常用的三大范式是,
第一范式(1NF)、第二范式(2NF)、第三范式(3NF)
。 - 第一范式:第一范式要求数据库表的每一列都是不可分割的原子数据项。(即两列数据不能放在同一列)
- 第二范式:第二范式在1NF的基础上,非主键属性必须完全依赖于主键(即消除部分依赖)。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键)。
- 第三范式:第三范式在2NF基础上,任何非主键属性不依赖于其它非主键属性(在2NF基础上消除传递依赖)
- 范式设计和反范式设计的对比
数据库操作
数据库操作(CRUD)通常分为两类DDL(Data Definition Language)数据定义语言和DML(Data Manipulation Language)数据操作语言。
按照功能可分为四大类:
数据定义语言(DDL)
DDL(Data Definition Language)数据定义语言用于定义和管理SQL 数据库中的所有对象的语言,对数据库中的某些对象(如,database,table)进行管理。
常用关键字:create、alter、drop、truncate、comment、grant、revoke等
数据操作语言(DML)
DML(Data Manipulation Language)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言。
常用关键字:select、update、delete、insert等
DML与DDL的区别
1、DML操作是可以手动控制事务的开启、提交和回滚。
2、DDL操作是隐性提交,不能rollback。
五、SQL的数据类型
数值型:
字符串型:
日期时间类型:
枚举类型:
数据库定义语句DDL、数据库操作语句DML
- 停止MySQL服务
语法:net stop 数据库服务的名字
例子:net stop mysql80 - 启动MySQL服务
语法:net start 数据库服务的名字
例子:net start mysql80
注:使用数据库之前必须要启动数据库服务,否则数据库不能提供访问 - 连接数据库命令
语法:mysql (-h 数据库的地址) -u 用户名 -p
例子:mysql -u root -p//连接本地数据库命令
例子:mysql -h 10.10.32.162 -u root -p - 退出数据库命令
语法:quit
- 修改用户密码
语法:alter user '用户名'@'数据库IP地址' identified with mysql_native_password by 新密码;
例子:
ALTER USER ‘root’@‘localhost’ IDENTIFIED WITH mysql_native_password BY ‘123456’; - 数据库定义DDL语法
- 查询当前数据库中的全部数据库
语法:show databases;
- 创建数据库
语法:create database [if not exists] 新数据库的名字;
例子:create database test3;
例子:create database if not exists test3;
注:if not exists,关键词是用来覆盖创建的。 - 删除数据库
语法:drop database [if exists] 删除数据库的名字;
例子:drop database test3;
注:if exitst,关键词,如果数据库不存在,不会报错,SQL语句可以正常执行。
- 查询当前数据库中的全部数据库
- 查看当前使用的数据库
语法:select database();
- 使用数据库命令/切换数据库命令
语法:use 数据库名字;
例子:use test4; - 查询数据库中的所有表格
语法:show tables;
- 创建数据库的表
语法:create table 表名( 字段名 字段类型 [字段约束] [默认值] [描述], 字段名 字段类型 [字段约束] [默认值] [描述]..., 外键约束 [FOREIGN KEY] [(当前表的列名)] REFERENCES 外表名 (外表列名) [删除修改是否联动] );
- 关键词
auto_increment
——自动增加,不能定义表级约束not null
——非空,不能定义表级约束default
——默认值,不能定义表级约束unique
——唯一。check
——限制列的取值范围,使用方式check(条件)primary key
——主键,即非空唯一。foreign key
——外键,使用方式,[foreign key] [列名] references 外表名 外表列名 [删除修改是否联动]
例子:
创建部门表
CREATE TABLE sdept(
sdeptno VARCHAR(100) PRIMARY KEY DEFAULT(UUID()),
sdeptname VARCHAR(100) NOT NULL UNIQUE
);
创建学生表
create table if NOT EXISTS users(
userid varchar(100) primary key default(UUID()),
username varchar(100) not null unique,
pwd varchar(100) not null default(123456),
isadmin bool default 0,
sex enum(‘男’,‘女’) DEFAULT(‘男’),
create_time datetime default(current_timestamp
),
sdeptno varchar(100),
FOREIGN KEY (sdeptno) REFERENCES sdept(sdeptno)
);
注:if not exists,使用关键词,即使表存在也会执行成功,但是不会覆盖创建表格。
- 关键词
- 查询表结构命令
语法:desc 表名;
例子:desc users; - 修改表结构命令
语法一:alter table 表名 change 原字段名字 新字段名 字段类型 字段约束;
例子: alter table users change sex sex1 varchar(100) not null default(‘男’);
语法二:alter table 表名 modify 字段的名字 字段类型 字段约束;
例子:alter table users modify sex1 enum(‘男’,‘女’) not null default(‘男’); - 添加表结构
语法:alter table 表名 add 新字段名 字段类型 字段约束;
例子:alter table users add interest varchar(100) unique; - 删除表结构
语法:alter table 表名 drop 字段名;
例子:alter table users drop interest; - 删除表(多表
,
隔开)
语法:drop table [表名];
例子:drop table users; - 数据库操作DML语句(字段值:
'字符串'
)- 插入语句–insert(一一对应,可一次性插入多条记录)
语法一:insert [into] 表名(字段名) values/value (字段的值);
例子:
insert sdept values(UUID(),‘软件工程’);//插入全部字段
insert sdept(sdeptname) value(‘计算机科学与技术’);//插入部分字段
语法二:insert [into] 表名(字段名字) values/value (字段的值1),(字段的值2),(字段的值3),(字段的值4);
例子:insert sdept(sdeptname) value(‘网络工程’),(‘物联网工程’),(‘通信工程’);
例子:insert users(username,sdeptno) value
(‘小包’,‘c5f28aec-200a-11eb-81f8-d43a6508d4a8’),
(‘小李’,‘c5f28aec-200a-11eb-81f8-d43a6508d4a8’),
(‘小刘’,‘c5f28aec-200a-11eb-81f8-d43a6508d4a8’),
(‘小朱’,‘c5f28aec-200a-11eb-81f8-d43a6508d4a8’),
(‘小张’,‘c5f28aec-200a-11eb-81f8-d43a6508d4a8’); - 更新语句----update
语法:update 表名 set 字段名=字段的值 [where 条件语句]
例子:
修改全部密码为1234
update users set pwd=1234;
指定条件修改
update users set isadmin=true where username=‘小包’; - 删除语句 —delete
语法:delete from 表名 [where 条件];
例子:
delete from users;//清空表中全部数据
delete from users where username=‘小包’;
delete from users where username=‘小李’ or username=‘小张’;
delete from users where username=‘小刘’ and username=‘小朱’; - 查询语句----select
语法一:select * from 表名;
查询表的全部数据
例子:select * from users;
语法二:select 字段名,字段名 from 表名;
查询指定字段
例子:select username,pwd from users;
语法三:select 字段名,字段名 from 表名 where 查询条件;
根据指定条件查询字段
例子:select username,pwd from users where username=‘小朱’;
语法四:select * from 表名 group by 分组字段;
根据分组字段查询分组后的第一条数据
例子:select * from users group by isadmin;
语法五:select * from 表名 order by 排序字段 顺序关键词;
按照顺序关键词进行查询数据
顺序关键词包含ASC(升序,默认值),DESC(降序)
例子:select * from users order by username ASC;
语法六:select * from 表名 group by 分组字段 having 条件;
根据分组后的结果进行条件查询,注:Having关键词必须要结合分组关键词使用
例子:select * from users group by isadmin having username=‘小朱’;
语法七:select distinct(去重的字段) from 表名;
例子:select distinct(isadmin) from users;
语法八:
select * from 表名 limit 查询数据的条数;
一个参数的语法
select * from 表名 limit 起始位置,查询数据的条数;
一个参数的语法
例子:select * from users limit 2;
select * from users limit 1,2;
注:“=”可以用其它比较运算符替换如“>、<、>=、<=、!= “等。若是多条件查询,可以使用between…and….., in(),like ’_’或like ’%’也可以使用or、and等逻辑运算符。
注:LIMIT通常和ORDER BY一起配合使用来进行记录的分页显示,且limit属于MySQL扩 展SQL92后的语法,其他数据库不通用
- 聚合函数
语法一:求和函数sum(字段名)
例子:SELECT SUM(pwd) FROM users
语法二:统计个数函数count(字段名)
例子:SELECT COUNT(pwd) FROM users
语法三:统计当前字段的最大值max(字段名)
例子SELECT max(num) FROM users
语法四:统计当前字段的最小值min(字段名)
例子:SELECT min(num) FROM users
语法五:求平均值avg(字段名)
例子:SELECT avg(num) FROM users
语法六:合并查询两列CONCAT(字段名1,字段名2)
例子:SELECT CONCAT(username,pwd) from users
注意:having和where的区别在于having是对聚合后的结果进行条件的过滤,而where是在聚合前就对记录进行过滤
- 重名名字段
语法:select 原字段名 [as] 新字段名 from 表名
例子:SELECT CONCAT(username,pwd) AS ‘用户信息’ from users - 表的连接
- 表的连接分为内连接和外连接,
- 内连接指:查询表与表之间的所有有关系的数据 ,进行排列组合查询,仅选出两张表中互相匹配的记录
- 例子 SELECT * from users
INNER JOIN
sdept - 外连接:查询表与表之间所有有关系的数据,会选出其他不匹配的记录
- 外连接分为两类:左连接和右连接
- 左连接:包含所有的左边表中的记录甚至是右边表中没有和它匹配的记录
- 右连接:包含所有的右边表中的记录甚至是左边表中没有和它匹配的记录
- 左连接的语法:
select * from 左表名 left join 右表名 on 关系条件
例子:select * from users LEFT JOIN sdept on users.sdeptno=sdept.sdeptno; - 右连接的语法:
select * from 左表名 right join 右表名 on 关系条件
例子:select * from users RIGHT JOIN sdept on users.sdeptno=sdept.sdeptno; - 左右连接的区别:左连接是以左表为基准查询右表的数据,右连接是以右表为基准查询左表的数据。
- 外连接分为两类:左连接和右连接
- 表的连接分为内连接和外连接,
- 子查询:
- 常见关键词:in、not in、=、!=、exists、not exists等
in(查询多个),=(查询一个)
例子:SELECT * from category WHERE father_idin
(
SELECT category_id from category WHERE father_id=
(
SELECT category_id FROM category WHERE father_id is NULL AND category_name=‘男装’) and category_name=‘男士外套’
)
- 联合查询:即将两个查询结果,按照一定的规则进行合并显示。
- 关键词
UNION ALL
,UNION [DISTINCT]
- 作用:将两条sql语句的结果合并输出。all,只会合并 ,DISTINCT会去掉重复的数据
例子:
SELECT * FROM users WHERE pwd=‘123456’ UNION SELECT * FROM users WHERE isadmin=‘0’;
- 关键词
- 练习
- 插入语句–insert(一一对应,可一次性插入多条记录)
-- SELECT goods_name,category_name from goodlist,category WHERE goodlist.category_id in(
-- SELECT category_id from category WHERE father_id in(
-- SELECT category_id from category WHERE father_id=(
-- SELECT category_id FROM category WHERE father_id is NULL AND category_name='男装') and category_name='男士外套'
-- )
-- ) and goodlist.category_id=category.category_id;
-- SELECT * from category WHERE father_id in(
-- SELECT category_id from category WHERE father_id=(
-- SELECT category_id FROM category WHERE father_id is NULL AND category_name='男装') and category_name='男士外套'
-- )
-- SELECT * from category c1 LEFT JOIN category c2 on c1.father_id=c2.category_id WHERE c2.category_name='男装'
-- -- 右表
-- (SELECT * FROM category WHERE category_name='男装') c1
-- -- 左表 category
-- SELECT c1.category_name '一级分类',c2.category_name '二级分类' from (SELECT * FROM category WHERE category_name='男装') c1 LEFT JOIN category c2 on c2.father_id=c1.category_id
- 查询语句中的常用关键词执行顺序
1、FROM
2、WHERE
3、GROUP BY
4、HAVING
5、SELECT
6、ORDER BY