本章节主要来了解http的相关知识点,那么长话短说,直接看内容。
基本写法
// (第一步)初步实现服务器功能 http
const http = require("http");
// (第二步)创建服务器实例对象
let server = http.createServer();
// (第三步)绑定请求事件
server.on("request",(req,res)=>{
res.end("hello");
});
//(第四步) 监听端口
server.listen(3000);
上述代码的功能就是,监听了一个端口,访问时页面输出hello。可以说着四步都是必须的步骤。
我们通过http.createServer方法创建了一个服务器实例对象,该方法返回的是一个http.server对象,第三步和第四步都是通过该对象去绑定request时间和设置监听端口号。
简写
http.createServer((req,res)=>{
res.end("hello");// 等价 res.write("ok"); res.end();
}).listen(4000,"192.168.0.103",()=>{
console.log("app is running...");
});
createServer
我们可以看到createServer多了一个回调信息,req其实是http.IncomingMessage的实例对象,res就是http.ServerResponse的实例对象。
req可以来获取发起请求的客户端的信息
res就是来响应客服端发起的请求。
正如res.end("hello"),返回客服端一个hello的数据,并且完成该响应。
listen
方法里有三个参数,第一个表示端口号,第二个表示ip地址,第三个表示服务被开启时的回到。
这里我们将上诉代码放置demo.js里面,首先需要在命令行里输入node demo.js,来开启服务,命令行中就会出现app is running 的输出,接着在浏览器里输入 192.168.0.103:4000 那么浏览器则会输出hello
我们上面也说了req是http.IncomingMessage的实例对象 res是http.ServerResponse的实例对象,前者是用来获取请求的对象,后者是用来响应结果的。先上代码
/*
处理请求路径分发
req对象是http.IncomingMessage的实例对象
res对象是http.ServerResponse的实例对象
*/
const http = require("http");
http.createServer((req,res)=>{
//req.url获取URL中的路径----端口之后的部分
console.log(req.method);
if(req.url.startsWith("/index")) {
// write作用向客户端响应内容 可以写多次
res.write("hello");
res.write("node.js");
res.end();// 只能执行一次 表示完成响应
} else if(req.url.startsWith("/about")) {
res.end("about");
} else {
res.end("sorry");
}
}).listen(3000,()=>{
console.log("app is running...");
});
我们重点来看看createServer的回调函数部分。
因为req是一个对象,我们可以通过req来获取请求的地址,请求方式,header头等信息,我们可以通过req.url方式来区分需要请求的地址从而响应不同的数据。如请求的url是/index则返回hellonode.js /about则返回的是about 两个都不是则返回sorry。
res.write 和 res.end都是来返回数据的,不过两者还是有区别的,write可以向客户端响应多次内容,end只能响应一次也表示这改次响应以及结束。
当然我们也可以通过fs模块去返回一个html页面的内容,那么浏览器就会出现出一个html页面的效果
比如说我们是要响应一个html类型的数据,那么响应的时候不就是显示一张网页吗?再如果说我们需要响应一个css文件里的样式内容,那么浏览器里的显示的内容就是css的内容,还有文本类型js类型jsp类型等等,这些不同的类型我们当然需要依据业务逻辑去动态的修改,
下面我们来通过一个案例来理解学习一下,大致的逻辑就是通过fs模块去获取url的后缀名字,通过后缀去获取相应的相应类型,再通过fs模块去获取对应文件的内容,从而输入相应的内容。
//引用模块
const http = require("http");
const fs = require("fs");
const path = require("path");
const mime = require("./mime.json");
function readFile(res,ext,name){
let datatype ="text/plain;charset=utf8";
if(mime[ext]){
res.writeHead(200, { 'Content-Type': mime[ext] });
}else{
res.writeHead(200, { 'Content-Type': 'text/plain' });
}
let readpath = __dirname+"/www/"+name+ext;
fs.readFile(readpath,"utf8",(error,data)=>{
if(error){
res.end("页面被妖怪抓走了");
}else{
res.end(data);
}
})
}
http.createServer((req,res)=>{
let httpurl = req.url;
let httpext = path.extname(req.url);
let httpname = path.basename(httpurl, httpext);
readFile(res,httpext,httpname);
}).listen(3000,()=>{
console.log("server is running");
})
mime.json
{
".html":"text/html",
".htm":"text/html",
".css":"text/css",
".jpg":"image/jpeg",
".js":"application/x-javascript",
".zip":"application/zip",
".json":"application/json"
}
我们可以看到,我们先获取了后缀名和文件名,然后去判断响应类型,若有这设置对应的类型,若没有则显示默认的text类型,在通过fs和path模块去获取对应文件的内容,从而显示出来。
在这里我们可能会发现,我们在浏览器请求一次但是node里面会接受两次,这是为什么呢?我们可以获取url来看看。
第一个是我们输入的正常请求,那么第二次的请求地址是 /favicon.ico 其实这是一个获取icon的地址,获取的是浏览器上面的图标
我们可以将查找文件,解析数据那部分进行分装,大家是否还记得exprots和module.exports的区别?
const fs = require("fs");
const path = require("path");
const mime = require("./mime.json");
// 封装根据不同路径读取文件,并把文件内容响应到浏览器
exports.staticreadfile = (res,ext,name)=>{
let datatype ="text/plain;charset=utf8";
if(mime[ext]){
res.writeHead(200, { 'Content-Type': mime[ext] });
}else{
res.writeHead(200, { 'Content-Type': 'text/plain' });
}
let readpath = __dirname+"/www/"+name+ext;
fs.readFile(readpath,"utf8",(error,data)=>{
if(error){
res.end("页面被妖怪抓走了");
}else{
res.end(data);
}
})
}
我们主要用到url模块的parse和format方法
parse
主要就是将一个url地址解析成一个对象,使用如下
const url = require("url");
let str = "http://www.baidu.com/abc/qs?name1=v1&name2=v2";
let res = url.parse(str,false);
console.log(res);
输出 Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com',
port: null,
hostname: 'www.baidu.com',
hash: null,
search: '?name1=v1&name2=v2',
query: 'name1=v1&name2=v2',
pathname: '/abc/qs',
path: '/abc/qs?name1=v1&name2=v2',
href: 'http://www.baidu.com/abc/qs?name1=v1&name2=v2' }
let res = url.parse(str,true);
输出 Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com',
port: null,
hostname: 'www.baidu.com',
hash: null,
search: '?name1=v1&name2=v2',
query: { name1: 'v1', name2: 'v2' },
pathname: '/abc/qs',
path: '/abc/qs?name1=v1&name2=v2',
href: 'http://www.baidu.com/abc/qs?name1=v1&name2=v2' }
parse第二个参数默认是false,当传入第二个参数true 表示把query的字符串值转成对象。
format
那么相对于的format就是将上诉的对象转化为地址。
这样一来我们就可以通过该方法去解析客户端通过get请求传来的数据
我们直接先上代码吧
const http = require("http");
const qs = require("querystring");
http.createServer((req,res)=>{
let pdata = "";
req.on("data",(chunk)=>{
// 每次获取一部分数据
pdata += chunk;
});
req.on("end",()=>{
console.log(pdata);
let obj = qs.parse(pdata);
res.end(obj.uname+"@"+obj.psw);
});
}).listen(3000,()=>{
console.log("app is running....");
});
我们先来了解querystring这个模块是干什么用的?在get请求中url模块的parse方法的第二个参数,决定是否将对象内部的query的数据转成对象的内部实现其实是通过querystring来实现的。
我们需要去捕获数据,去监听data和end,将捕获到的数据连接成一个字符串,该数据就例如 name1=v1&name2=v2 然后我们通过querystring模块的parse方法将字符串转化为对象。