目录
http模块
方式1
var http=require("http")
http.createServer((req,res)=>{
res.write("hello world");
res.end()
}).listen(3000,()=>{
console.log("server start");
})
注意:
- res.end()必须加,不然浏览器就会认为你未响应完,进而一直等,end里面也可以加字符串形式的参数,这里面的东西也会显示在浏览器中
- req代表浏览器发给你的请求,res代表你响应给浏览器的内容
- 监听3000端口,当收到请求就会执行回调函数
方式2
var http=require("http")
var server=http.createServer()
server.on("request",(req,res)=>{
res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"})
res.write("<h1>你好,世界</h1>")
res.end("结束")
})
server.listen(3000,()=>{
console.log("server start")
})
理解:
- 监听3000端口,当收到请求就会触发请求事件进而执行时间里面的内容
- res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"})因为是text/html格式所以下面的html标签可以被解析
综合案例
//获取http对象
var http=require("http")
//创建服务器
http.createServer((req,res)=>{
//与浏览器之间的交互
//req:浏览器传过来的参数
//res:给浏览器响应的参数
if(req.url==="/favicon.ico"){
return
}
res.writeHead(renderStatus(req.url),{"Content-Type":"text/html;charset=utf-8"})//因为是text/html格式所以下面的标签可以解析
res.write(renderHTML(req.url));//实现路径页面跳转
res.end("[1,2,3]")//结束响应里面的数组会显示在浏览器中,必须为字符串,也可以不填
}).listen(3000,()=>{
console.log("server start");
})//监听3000端口号
function renderHTML(url){
switch(url){
case "/home":
return `<h1>我是home</h1>`
case "/help":
return `<h1>我是help</h1>`
default: `<h1>404</h1>`
}
}
function renderStatus(url){
var arr=["/home","/help"]
return arr.includes(url)?200:404
}
jsonp
jsonp原理:动态创建script标签
前端
<script>
var a=document.createElement("script")
a.src="http://localhost:3000?callback=myuse"
document.body.appendChild(a)
function myuse(obj){
console.log(obj);
}
</script>
后端
var http=require("http")
var myurl=require("url")
var server= http.createServer()
server.on("request",(req,res)=>{
var urlobj=myurl.parse(req.url,true)
res.end(`${urlobj.query.callback}(${JSON.stringify({
name:"lili",
age:101
})})`)
})
server.listen(3000,()=>{
console.log("start");
})
理解:前端获取后端特定对象
http模块cors
后端内
res.writeHead(200,{"Content-Type":"text/html;charset=utf-8","access-control-allow-origin":"*"})
解释:后端允许*(所有域)通过控制
http模块get
注意:node既可以做服务器端开发,又可以做客户端开发
var http=require("http")
data=""
http.get("http://localhost:8888/test/second?name=lili",(res)=>{
res.on("data",(chunk)=>{
data+=chunk
})//data事件,只要有数据返回就会触发,(请求的地址是以数据流的方式一点点返回来的)
res.on("end",()=>{
console.log(data);
})//收集完数据后执行
})
后端servlet路径: http://localhost:8888/test/second
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
/* 允许跨域的主机地址 */
response.setHeader("Access-Control-Allow-Origin", "*");
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(name);
}
注意:
- 如果请求的地址协议是https地址那么就获取https对象
- data事件:只要有数据返回就会触发
- end事件:数据收集结束就会触发的事件
http模块post
var http=require("http")
data=""
var options={
hostname:"localhost",
port:"8888",
path:"/test/second",
method:"POST",
headers:{
"Content-Type":"application/json"
}
}
var req=http.request(options,(res)=>{
res.on("data",chunk=>{
data+=chunk
})
res.on("end",()=>{
console.log(data);
})
})
req.write(JSON.stringify({name:"lili"}))//带给后端的数据——发送的
req.end()
后端servlet路径: http://localhost:8888/test/second
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
request.setCharacterEncoding("utf-8");
BufferedReader reader = request.getReader();
String str,wstr="";
while ((str=reader.readLine())!=null) {
wstr += str;
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(wstr);
}
http模块爬虫
项目内:npm init
使用时需安装cheerio模块:npm -i --save cheerio
引入cheerio模块:var cheerio=require("cheerio")
var https=require("https")
const cheerio=require("cheerio")
const { json } = require("stream/consumers")
data=""
https.get("https://i.maoyan.com/",(res)=>{
res.on("data",(chunk)=>{
data+=chunk
})//data事件,只要有数据返回就会触发,(请求的地址是以数据流的方式一点点返回来的)
res.on("end",()=>{
spider(data)
})//收集完数据后执行
})
function spider(data){
let $=cheerio.load(data)//load原始数据
let $m=$(".column.content")//通过class选择器二次筛选
let movies=[]
$m.each((index,value)=>{
movies.push({
title:$(value).find(".title").text(),
grade:$(value).find(".grade").text()
})//经过类选择的条件后push到movies数组里面
})
console.log(movies);//收集好的内容
return JSON.stringify(movies)
}
注意:data为获取到的数据
url模块
url模块旧版用法
获取url模块对象:var url=require("url")
对请求的url进行解析:var urlobj=url.parse(req.url,true)
作用:可以将路径与参数进行分割操作等
获取路径:urlobj.pathname
获取参数:urlobj.query
注意:第二个参数传一个布尔值,默认为false,传true后,你获取的参数被封装成对象,容易操作
//当第二参数为true时的url对象结构
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?name=lili&age=90',
query: [Object: null prototype] { name: 'lili', age: '90' },
pathname: '/help',
path: '/help?name=lili&age=90',
href: '/help?name=lili&age=90'
}
将url对象转化为端口号后面的地址信息:var message=url.format(urlobj)
打印结果:/help?name=lili&age=90
resolve方法作用:进行地址字符串的拼接
获取url模块对象:const url=require("url")
var a=url.resolve("/one/two/three","four")
注意:纯路径情况下three后面不加/则three直接被four替代,若加/则直接相连
var b=url.resolve("http://example.com/","/one")
var c=url.resolve("http://example.com/one","/two")
注意:有域名时不管域名(到com)后面有什么,都会被,后面的第二个参数替换
const http=require("http")
const myurl=require("url")
http.createServer((req,res)=>{
var message=myurl.resolve(req.url,"one")
console.log(message);
res.end("ok")
}).listen(5000,()=>{
console.log("server start");
})
url模块新版用法
const myurl=new URL(req.url,"用来拼接的合法地址")
注意:其不需要引入旧版的url模块
路径的拼接
var b=new URL("/one","http://localhost:3000/home/dog")
console.log(b.href);
//http://localhost:3000/one
var c=new URL("one","http://localhost:3000/home/dog")
console.log(c.href);
//http://localhost:3000/home/one
拿到拼接后路径的结果:myurl.href
由此观之,其路径的拼接和旧版的resolve方法如初一辙,就是参数的位置换了
myurl结构
URL {
href: 'http://127.0.0.1:3000/help?name=lili&age=90',
origin: 'http://127.0.0.1:3000',
protocol: 'http:',
username: '',
password: '',
host: '127.0.0.1:3000',
hostname: '127.0.0.1',
port: '3000',
pathname: '/help',
search: '?name=lili&age=90',
searchParams: URLSearchParams { 'name' => 'lili', 'age' => '90' },
hash: ''
}
获取路径:var a=myurl.pathname
获取参数:var b=myurl.searchParams
注意:此种方法获取参数最终得到一个迭代器,可以用forof遍历
for (const [key,value] of myurl.searchParams) {
console.log(key,value);
}
新版format函数
引入url模块:const url=require("url")
语法:url.format(myurl,{参数1:boolean,参数2:boolean,……})
注意:新版format函数也需要引入url模块
options对象
测试url:http://a:b@测试?abc#foo
const http=require("http")
const url=require("url")
http.createServer((req,res)=>{
const myurl=new URL(req.url,"http://localhost:3000")
console.log(url.format(myurl,{auth:false,search:false}));
res.end("ok")
}).listen(3000,()=>{
console.log("server start");
})
http模块搭建服务获取参数
用于接收get请求
//引入http内置模块
var http=require("http")
//创建http服务
var server=http.createServer()
server.on("request",(req,res)=>{
//拼接并解析请求的url
const myurl=new URL(req.url,"http://localhost:3000/")
//获取请求url的参数迭代器
var b=myurl.searchParams
//对该迭代器进行遍历
for (const [key,value] of b) {
console.log(key,value);
}
//设置响应编码格式
res.setHeader("content-Type","text/html;charset=utf-8")
//设置返回内容
res.write("好吧,你已经成功了")
res.end()
})
server.listen(3000,()=>{
console.log("server start")
})
用于接收post请求
//引入http内置模块
var http=require("http")
//创建http服务
var server=http.createServer()
server.on("request",(req,res)=>{
let data="";
//接收post请求传递的参数
req.on("data",chunk=>{
data+=chunk
})
req.on("end",()=>{
console.log(data);
})
//设置响应编码格式
res.setHeader("content-Type","text/html;charset=utf-8")
//设置返回内容
res.write("好吧,你已经成功了")
res.end()
})
server.listen(3000,()=>{
console.log("server start")
})
发送post请求:http://localhost:3000
请求体:
{
"name": "lili"
}
querystring模块
引入:var querystring=require("querystring")
parse()
语法:var obj=querystring.parse(str)
作用:将get请求后?之后的一串字符转换为对象格式
var querystring=require("querystring")
var str="name=lili&age=100&location=dalian"
var obj=querystring.parse(str)
console.log(obj);
stringify()
语法:var mystr=querystring.stringify(myobj)
作用:将对象转换为用&拼接的一串字符(get请求?之后的串形式)
var querystring = require("querystring")
var myobj={
a:1,
b:2
}
var mystr=querystring.stringify(myobj)
console.log(mystr);
escape()
语法:var escaped=querystring.escape(str1)
作用:将get请求后?之后的一串字符内的特殊字符进行编码
var querystring = require("querystring")
var str1="id=3&city=北京&url=https://www.baidu.com"
var escaped=querystring.escape(str1)
console.log(escaped);
unescape()
语法:var str2=querystring.unescape(str2)
作用:将已经给特殊字符编码的一串字符进行解码
var querystring = require("querystring")
var str2="id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com"
var escape1=querystring.unescape(str2)
console.log(escape1);
内置模块event
导入模块:const eventemitter=require("events")
const eventemitter=require("events")
const event=new eventemitter()
// 监听play事件(play是随便写的)
event.on("play",(data,fin)=>{
console.log("事件触发了",data,fin);
})
//触发上面的play事件。
event.emit("play","多次","结束")
//emit第二个之后的参数都是上面回调函数的参数
path模块
主要功能:对路径进行处理
常用的魔术方法
- __filename:当前文件的根路径,包含文件名和路径
- __dirname:当前文件所在文件夹的根路径,只是路径部分,不包含文件名
console.log(__filename);
//C:\All\face\内置模块\path.js
console.log(__dirname);
//C:\All\face\内置模块
使用
引入path模块:const path=require("path")
dirname()
语法:path.dirname(“路径”)
返回值:当前文件所在的文件夹名称
作用:获取路径的目录部分
const path=require("path")
var p=path.dirname("C:\\All\\face\\内置模块\\path.js")
console.log(p);
//C:\All\face\内置模块
basename()
语法:path.basename("路径","扩展名")
返回值:当前路径最后的文件名
作用:获取路径的文件名
const path=require("path")
//一个参数
var p=path.basename("C:\\All\\face\\内置模块\\path.js")
console.log(p);//path.js
//2个参数
var p=path.basename("C:\\All\\face\\内置模块\\path.js",".js")
console.log(p);//path
注意:后面的第二个参数为可选参数,主要用于过滤掉文件的扩展名,其实就是如果有则过滤掉后面的一串字符,后面字符串如果没有全匹配则一个不过滤
join()
语法:path.join(path1,path2,……)
返回值:拼接后的完整路径
作用:把多个不同或相同格式的路径拼接成一个路径,并统一拼接格式
const path=require("path")
var path1="C:\\All\\face"
var path2="内置模块/path.js"
var str=path.join(path1,path2)
console.log(str);
//C:\All\face\内置模块\path.js
注意
- path可以支持不同格式路径的拼接并统一拼接格式
- join()方法中的路径参数头或尾部的斜杠可加可不加,方法均能识别出来
- 路径之间也可以与\\..\\连用,之后就会按照顺序来确定到底返回到哪一级的目录
文件操作模块
引入文件模块:const fs=require("fs")
创建文件目录
fs.mkdir("./avatar",(err)=>{
console.log(err);//如果创建失败则会打印错误
if(err&&err.code==="EEXIST"){
console.log("目录已经存在");
}
})
重命名文件目录
//将avatar目录重命名为av
fs.rename("./avatar","./av",(err)=>{
console.log(err);//修改失败则打印错误
})
注意:第一个参数为源目录名,第二个参数为新目录名
删除文件目录
fs.rmdir("./a",err=>{
console.log(err);
})
注意:该目录下面若有文件则会删除失败
在目录里创建文件
fs.writeFile("./app/a.txt","hello world",err=>{
console.log(err);
})
理解:在app目录里创建a.txt文件并在内部写hello world
关于writeFile函数,如果里面没有文件则创建文件往里面写入东西,如果有文件则把文件里的东西覆盖掉
fs.appendFile("./app/a.txt","hello world",err=>{
console.log(err);
})
关于appendFile函数,如果里面没有文件则创建文件往里面写入东西,如果有文件则往文件里追加内容
读取文件
fs.readFile("./app/a.txt",(err,data)=>{
if(!err){
console.log(data);
}
})
理解:读取app目录下的a.txt文件
读取该文件以utf-8格式
fs.readFile("./app/a.txt","utf-8",(err,data)=>{
if(!err){
console.log(data);
}
})
删除文件
//删除app目录下的a.txt文件
fs.unlink("./app/a.txt",err=>{
console.log(err);
})
查看目录/文件信息
fs.stat("./app",(err,data)=>{
console.log(data);//打印目录信息
console.log(data.isFile());//判断是不是文件
console.log(data.isDirectory);//判断是不是目录
})
读取目录中有多少文件
//该方法会把app目录下的所有文件名以数组的形式展示
fs.readdir("./app",(err,data)=>{
console.log(data);
})
删除目录以及目录中的文件
fs.readdir("./app",(err,data)=>{
data.forEach(item=>{
fs.unlinkSync(`./app/${item}`)
})
fs.mkdir("./app",err=>{
console.log(err);
})
})
理解:用同步方法目的,每次执行一遍foreach就会删除一个文件,直到文件删除完再遍历另一个文件
stream流模块
引入文件模块:const fs=require("fs")
stream是Node.js提供的又一个仅在服务器端可用的模块,目的是支持流着这种数据结构
用流读取文件
const rs=fs.createReadStream("./1.txt","utf-8")
rs.on("data",(chuck)=>{
console.log((chuck));
})
rs.on("end",()=>{
console.log("end");
})
rs.on("error",(err)=>{
console.log(err);
})//一旦读取流出现错误时就会执行error事件
理解:以utf-8的形式读取1.txt文件中的内容
用流写文件
const ws=fs.createWriteStream("./1.txt","utf-8")
ws.write("hello lili")
ws.end()
理解:将1.txt文件中写hello world
流读写文件
const fs=require("fs")
const rs=fs.createReadStream("./1.txt")
const ws=fs.createWriteStream("./2.txt")
rs.pipe(ws)
//创建可读流之后通过管道连接到可写流-将1.txt的内容写到2.txt中
注意:如果2.txt中有数据则会被覆盖
内置模块zlib模块
理解:当文件比较大的时候会进行压缩文件
引入zlib模块:const zlib=require("zlib")
const fs=require("fs")
const zlib=require("zlib")
const gzip=zlib.createGzip()
const rs=fs.createReadStream("./1.txt")
const ws=fs.createWriteStream("./2.txt")
rs.pipe(gzip).pipe(ws)
理解:将1.txt文件中的内容压缩后写入2.txt中
crypto模块
crypto模块的目的是为了提供通用的加密和哈希算法。用纯js代码去实现这些功能不是不可能,但速度会非常慢。node.js用c/c++实现这些算法后,通过cypto这个模块暴露为js接口,这样用起来方便,运行速度也快
引入crypto模块:const crypto=require("crypto")
MD5加密算法
MD5是一种常用的哈希算法,用于给任意数据一个签名,这个签名通常用一个16进制的字符表示
const crypto=require("crypto")
const hash=crypto.createHash("md5")
hash.update("hello world")//把一段字符进行一段MD5的hash转换
hash.update("hello node.js")//可任意多次调用
console.log(hash.digest("base64"));//打印加密后的结果——base64格式的
hmac算法
hmac算法也是一种hash算法,它可以利用md5或sha1等hash算法,不同的是,hmac算法还需要一个密钥只要密钥发生变化,那么输入同样的数据也会得到不同的签名,因此,可以把hmac理解为用随机数增强的哈希算法
const crypto=require("crypto")
const hash=crypto.createHmac("md5","secret-key")//注意:第二个参数为密钥,可以随意写
hash.update("123456")
console.log(hash.digest("hex"));
AES算法
AES是一种对称加密算法,加密与解密都用同一个密钥
const crypto=require("crypto")
//加密
function encrypt(key,iv,data){
let dep=crypto.createCipheriv("aes-128-cbc",key,iv)
return dep.update(data,"binary","hex")+dep.final("hex")
//三个参数:原始数据、输入数据的编码各式、输出数据的编码格式
}
//解密
function decrypto(key,iv,crypted){
//注意:第三个参数为加密后的数据
crypted=Buffer.from(crypted,"hex").toString("binary")
let dep=crypto.createDecipheriv("aes-128-cbc",key,iv)
return dep.update(crypted,"binary","utf8")+dep.final("utf8")
}
//加密
let key="abcdef0123456789"
let iv="0123456789abcdef"
let data="helloworld"
let cry=encrypt(key,iv,data)
console.log(cry);
//解密
let decrypted=decrypto(key,iv,cry)
console.log(decrypted);
扩展路由案例
const http = require("http")
const fs = require("fs")
http.createServer((req, res) => {
const myurl = new URL(req.url, "http://127.0.0.1")
switch (myurl.pathname) {
case "/login":
res.writeHead(200, { "Content-Type": "text/html;charset=utf8" })
res.write(fs.readFileSync("./static/login.html"), "utf-8")
break
case "/home":
res.writeHead(200, { "Content-Type": "text/html;charset=utf8" })
res.write(fs.readFileSync("./static/home.html"), "utf-8")
break
default:
res.writeHead(404, { "Content-Type": "text/html;charset=utf8" })
res.write(fs.readFileSync("./static/404.html"), "utf-8")
}
res.end()
}).listen(3000, () => {
console.log("server start");
})