什么是Node.js
Node.js就是运行在服务器端的JavaScript,是现在流行的语言中能同时运行在前端与后台的程序语言。
Node.js优势
常见的后台开发语言:
1.PHP
2.Java
3.Python
每个语言都有它自己的特点和优势。
Node.js的优势:
1.Node.js开发高效,代码简单
2.跟前台JS配合方便
3.Node.js便于前端学习,根据JavaScript语言,不用再单独新学一门陌生的语言。
安装Node.js
去官网下下载最新版本的Node.js。
在终端运行js
1.切换盘符
2.cd xxx 进入目录
3.node xxx.js(文件名)运行js文件
初尝试:使用node.js创建简易服务器
nodejs提供了模块,自身就可以用来构建服务器。
下面就使用它创建一个简易的http服务器
// 引入http
const http = require('http');
// 有了http就可以让它创建服务器
var server = http.createServer(function(req,res){
console.log('有人来了');
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);
运行服务器,浏览器访问 http://localhost:8080/,控制台输出如下:
代码思路:首先引入http
模块,然后使用模块里的createServer
方法返回了一个http.Server
对象,然后为其添加事件监听
,这里的8080为端口号
(可随意自行修改,只要端口不被占用)。
尝试II:使用request和response参数
在初尝试中使用的createServer
方法有一个参数——一个回调函数。
这个回调函数的作用很简单,每当有人连接服务器来访问的时候,执行回调函数作个标识。
在这个回调函数中,有2个参数:request
和response
参数名 | 含义 |
---|---|
request | 请求 输入—请求的信息 |
response | 响应 输出—输出的东西 |
1.服务器会创建response
对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应。
2.服务器会把客户端的请求数据封装到request
对象中,request
就是请求数据的载体。
参数response的应用
有了response
,我们就可以实现服务器对客户端的响应,向客户端输出数据。
// 引入http
const http = require('http');
var server = http.createServer(function(req,res){
// 向浏览器(前台)输出东西
res.write("hello world");
//结束
res.end()
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);
运行服务器,浏览器访问可以看到页面上出现了"hello world"
代码思路:response.write
方法可以向前端输出数据,但是需要注意的是,必须调用response.end
方法结束请求,否则前端会一直处于等待状态。
参数request的应用
有了request
,服务器可以根据客户端的不同请求,响应客户端所发的请求。
const http = require('http');
var server = http.createServer(function(req,res){
// /favicon.ico chrome浏览器自己加上请求的
// console.log(req.url);
switch (req.url) {
case '/1.html':
res.write("111");
break;
case '/2.html':
res.write("222");
break;
default:
res.write("404")
break;
}
// 结束
res.end();
});
// 监听——等着
// 端口——数字
// http://localhost:8080/
server.listen(8080);
运行服务器,浏览器请求访问端口下不同的html网页,服务器向浏览器输出相应的内容。
代码思路:req.url
获取用户请求的路径,使用switch
方法根据不同的请求路径做出不同的响应(res.write
)。
尝试III:服务器访问磁盘文件
当页面数量很多时,使用switch
语句工作量大而且每次增加请求路径时,都需要重启服务器。
为了解决这个问题,需要将页面文件丢到磁盘,让服务器访问磁盘的文件。
而这就需要用到node.js提供的模块了。
在使用模块进行文件操作时,首先来了解异步和同步的区别:
异步——多个操作可以同时进行,前一次的操作没完事,后一次也能开始。
同步——一次一个
知道它们的区别后,我们知道
1.服务器肯定是异步
的,来多少用户都能同时满足,同时服务。
2.因为文件操作是非常缓慢的操作,如果是同步的就乱套了,服务器就只专门读文件了。所以需要异步操作
,既然是异步操作,就需要有回调函数告诉它什么时候结束文件操作。
在模块中提供了异步版本
的读写文件方法:
readFile(文件名,function(err,data){})
writeFile(文件名,function(err){})
简单使用readFile
我们可以使用方法读取文件,该方法有2个参数:要读取的文件名和回调函数。回调函数有2个参数:err
和data
(err
是返回的错误信息,data
则是返回的数据)
//aaa.txt
aaasd
asdasd
123321
12asda
123123
//引入fs模块
const fs = require('fs');
// readFile(文件名,回调函数)
fs.readFile("aaa.txt",function(err,data){
if (err) {
console.log("读取失败");
}else{
// 控制台输出原始的二进制数据
console.log(data);
//使用toString方法,控制台输出非二进制数据
console.log(data.toString());
}
})
简单使用writeFile
我们可以使用方法写入文件,该方法有3个参数:要写入的文件名,写入的内容和回调函数。回调函数只有1个参数:err
(err
是返回的错误信息)
const fs = require('fs');
//writeFile(文件名,内容,回调)
fs.writeFile("bbb.txt","hello world",function(err){
console.log(err);
});
应用
了解和学会使用和后,尝试修改服务器代码,让服务器访问磁盘的文件。
1.创建www目录用于存放文件
2.server.js
const http = require('http');
const fs = require('fs');
var server = http.createServer(function(req,res){
// req.url => '/index.html'
// 读取 => './www/index.html'
// './www'+req.url
var file_name = './www'+req.url;
fs.readFile(file_name,function(err,data){
if(err){
console.log('bbbb');
res.write('404');
}else{
console.log('bbbb');
// 机器能识别二进制文件
res.write(data);
}
});
console.log('aaaa');
res.end();
});
server.listen(8080);
3.运行
这样写在运行时会报错,而且还会发现控制台先输出a再输出b
因为readFile
为异步操作
,不会等着它完成,发送完readFile
请求直接就开始执行end()
了,所以这时候就出问题了。
==举个例子
==
oBtn.onclick = function(){
alert('a');
}
alert('b');
肯定是先输出b再输出a,因为a需要用户点击了才会输出。
==举个例子
==
解决方法:调整res.end()位置,等待文件读取完才执行
fs.readFile(file_name,function(err,data){
if(err){
res.write('404');
}else{
// 机器能识别二进制文件
res.write(data);
}
res.end();
});
4.在不中断服务器的前提下,服务器一直运行,向www目录再加个文件2.html,然后访问。
服务器接收数据请求
前台发送数据请求大概有以下几种方式:
- form表单
- ajax
- jsonp
后台不管这些数据请求是以何种方式发送过来的,一视同仁。在浏览器中,前台与后台之间的交互永远都是通过http协议,所以对于后台来说,它只是接收到了一个http请求。
http请求主要有两大方式:
- GET 数据在url中一起传输
- POST 数据不在url中
接收GET数据
先准备一个表单form.html,发送GET数据请求
<!DOCTYPE html>
<head>
<title>from</title>
</head>
<body>
<form action="http://localhost:8080/" method="GET">
用户:<input type="text" name="user" value=""><br>
密码:<input type="password" name="pass" value=""><br>
<input type="submit"id="提交">
</form>
</body>
</html>
创建服务器,通过req获取前台请求数据,res返回响应结果
const http = require('http');
var server = http.createServer(function(req,res){
// 通过req获取前台请求数据
console.log(req.url);
// res返回响应结果
res.setHeader("Content-Type", "text/html; charset=utf-8")
res.write("请求成功");
res.end();
});
server.listen(8080);
填写表单然后提交,可以看到页面跳转以及url中有form提交过来的数据
GET数据解析
可以用比较笨的方法处理res.url包含的数据(使用split进行切割)
const http = require('http');
var server = http.createServer(function(req,res){
var GET = {};
// 通过req获取前台请求数据,
// /?user=abcdef&pass=123456
// /favicon.ico
//console.log(req.url);
// 去除掉chrome自加的req.url=> /favicon.ico 避免报错
if(req.url.indexOf('?')!=-1){
var arr = req.url.split('?');
// arr[0] => 地址 "/"
var url = arr[0];
//arr[1] => "user=abcdef&pass=123456"
var arr2 = arr[1].split('&');
// arr2=>["user=abcdef","pass=123456"]
for(var i=0;i<arr2.length;i++){
var arr3 = arr2[i].split("=");
//arr3[0] => 名字 'user'
//arr3[1] => 数据 'abcdef'
GET[arr3[0]] = arr3[1];
}
}else{
var url = req.url;
}
console.log(url,GET);
res.write('success');
res.end();
});
server.listen(8080);
再次提交表单数据可以看到
如果每次都要一次次切割处理数据太麻烦了,node.js提供了一个模块专门解析user=abcdef&pass=123456
这种格式的。
const querystring = require('querystring');
var json = querystring.parse("user=abcdef&pass=123456");
console.log(json);
可以看到querystring.parse
方法成功解析了数据。我们可以使用它简化上面代码。
const http = require('http');
const querystring = require('querystring');
var server = http.createServer(function(req,res){
var GET = {};
if(req.url.indexOf('?')!=-1){
var arr = req.url.split('?');
// arr[0] => 地址 "/"
var url = arr[0];
GET = querystring.parse(arr[1]);
}else{
var url = req.url;
}
console.log(url,GET);
res.write('success');
res.end();
});
server.listen(8080);
虽然使用模块可以简化一堆split切割语句,但是一开始还是得切割一下url。node.js提供了一个比模块还简便的模块——专门用来解析URL地址。
url.parse方法如果设置参数为true时将使用查询模块分析查询字符串:
默认为false时,则返回:
const http = require('http');
const urlLib = require('url');
var server = http.createServer(function(req,res){
var obj = urlLib.parse(req.url,true);
var url = obj.pathname;
var GET = obj.query;
console.log(url,GET);
res.write('success');
res.end();
});
server.listen(8080);
接收POST数据
POST数据也是从request对象过来的,但是它比GET数据大得多,这就导致在处理上有一些小小的区别——POST数据需要分段发送。原因如下:
①POST数据如果是一次性把数据发送过来,别的数据就得等它完全走完了才能传过来。
②在传输过程时,数据有可能出现错误,如果是分段的话,将错误的一小段重发就行了,无须重发一整个大数据。
因为POST数据是分段发送,我们就得分段接收,这就要用到res.on('data',func)
和res.on('end',func)
- data 每当data事件发生一次的时候,说明有一段数据已经到达
- end 每当end事件发生时,说明全部数据已经到达
先准备一个表单form.html,发送POST数据请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/aaa" method="POST">
用户:<input type="text" name="user"><br>
密码:<input type="password" name="pass" ><br>
<input type="submit" value="提交">
</form>
</body>
</html>
创建服务器server.js
const http = require('http');
http.createServer(function(req,res){
var str = '';//接收数据
// data——每当有一段数据到达会发生时就会发生一次(很多次)
var i = 0;
req.on('data',function(data){
console.log(第${i++}次收到这个数据
);
str += data;
});
// end——数据全部到达(一次)
req.on('end',function(){
console.log(str);
});
}).listen(8080);
可以看到:
①POST数据和GET数据的格式一样;
②当POST数据量小时,一次就能发送完。
修改表单内容,添加一个文本框
<form action="http://localhost:8080/aaa" method="POST">
用户:<input type="text" name="user"><br>
密码:<input type="password" name="pass" ><br>
<textarea name="textarea" cols="40" rows="8"></textarea>
<input type="submit" value="提交">
</form>
可以看到当数据量变大时,它就会进行分段。
POST数据解析
从结果看,已经知道POST数据和GET数据的格式一样,就可以用前面使用的模块解析POST数据。
req.on('end',function(){
var POST = querystring.parse(str);
console.log(POST);
});
阶段复习
将上面所学知识进行一个整合,从接收请求到读取文件重新搭建一遍服务器作为复习。
//引入模块
const http = require('http');
const fs = require('fs');
const querystring = require('querystring');//主要用来解析POST数据
const urlLib = require('url');//主要用来解析GET数据
var server = http.createServer(function(req,res){
//获取并解析GET数据
var obj = urlLib.parse(req.url,true);
//请求的url地址
var url = obj.pathname;
//GET数据
const GET = obj.query;
//获取POST数据
var str='';//空字符串存储POST数据(并不是标准做法)
req.on('data',function(data){
// 拼接字符串
str+=data;
});
req.on('end',function(){
//解析POST数据
const POST = querystring.parse(str);
})
/*
url —— 要什么 哪个文件
GET —— GET数据
POST —— POST数据
*/
//文件请求
// 获取文件名
var file_name='./www'+url;
// 打开文件
fs.readFile(file_name,function(err,data){
if(err){
res.write('404');
}else{
res.write(data);
}
res.end();
})
});
server.listen(8080);
应用:实现用户注册和登录
根据目前所学知识,实现用户注册和登录功能与服务器进行数据交互。实现之前必须先了解创建接口
例如:/user?act=reg&user=aaa&pass=123456
服务器则返回给前台一个JSON:{"ok":false,"msg":"原因"}
/user?act=login&user=aaa&pass=123456
服务器则返回给前台一个JSON:{"ok":false,"msg":"原因"}
需要准备一个ajax库来使用,放在www目录下ajax.js,用于前台ajax请求服务器。
function json2url(json){
var arr=[];
for(var name in json){
arr.push(name+'='+json[name]);
}
return arr.join('&');
}
function ajax(json){
json=json || {};
if(!json.url)return;
json.data=json.data || {};
json.type=json.type || 'get';
var timer=null;
if(window.XMLHttpRequest){
var oAjax=new XMLHttpRequest();
}else{
var oAjax=new ActiveXObject('Microsoft.XMLHTTP');
}
switch(json.type){
case 'get':
oAjax.open('GET',json.url+'?'+json2url(json.data),true);
oAjax.send();
break;
case 'post':
oAjax.open('POST',json.url,true);
oAjax.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
oAjax.send(json2url(json.data));
break;
}
oAjax.onreadystatechange=function(){
if(oAjax.readyState==4){
clearTimeout(timer);
if(oAjax.status>=200 && oAjax.status<300 || oAjax.status==304){
json.success && json.success(oAjax.responseText);
}else{
json.error && json.error(oAjax.status);
}
}
};
}
ajax主要有几个参数需要了解一下
ajax({
url:'xxx',
data:{xxx},
type:'get/post'
success:
error:
})
初始化服务器server.js
const http = require('http');
const fs = require('fs');
const querystring=require('querystring');
const urlLib = require('url');
var server = http.createServer(function(req,res){
//解析数据
var str = '';
req.on('data',function(data){
str+=data;
});
req.on('end',function(){
var obj = urlLib.parse(req.url,true);
const url = obj.pathname;
const GET = obj.query;
const POST = querystring.parse(str)
// 读取文件
var file_name = './www'+url;
fs.readFile(file_name,function(err,data){
if(err){
res.write('404');
}else{
res.write(data);
}
res.end();
});
})
})
server.listen(8080);
需要将访问分为两大类:
-
对文件的访问
-
对接口的访问
这个应用只有一个user接口,所以可以这么写
//区分访问——接口、文件
if(url=='/user'){//接口
}else{//文件
// 读取文件
var file_name = './www'+url;
fs.readFile(file_name,function(err,data){
if(err){
res.write('404');
}else{
res.write(data);
}
res.end();
});
}
因为是个简单应用,一切逻辑与操作从简,这里使用变量存储用户数据
var users={};//{"blue":"123456","zhangsan":"123456"}
当访问接口时,根据它的act作出不同的响应:是注册reg还是登录login或者其他……
if(url=='/user'){//接口
switch(GET.act){
case 'reg':
//1.检查用户名是否已经有了
if(users[GET.user]){
res.write('{"ok":false,"msg":"此用户已存在"}');
}else{
//2.插入users
users[GET.user] = GET.pass;
res.write('{"ok":true,"msg":"注册成功"}');
}
break;
case 'login':
// 1.检查用户是否存在
if(users[GET.user]== null){
res.write('{"ok":false,"msg":"此用户不存在"}');
// 2.检查用户密码
}else if(users[GET.user]!=GET.pass){
res.write('{"ok":false,"msg":"用户名或密码有误"}');
}else{
res.write('{"ok":true,"msg":"登录成功"}');
}
break;
default:
res.write('{"ok":false,"msg":"未知的act"}');
}
res.end();
}
后台服务器就搭建完了,接下来做前台
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入ajax -->
<script src="ajax.js" charset="utf-8"></script>
<!-- ajax不能跨域请求,得这样访问http://localhost:8080/user.html -->
<script type="text/javascript">
window.onload = function(){
var oTxtUser = document.getElementById('user');
var oTxtPass = document.getElementById('pass');
var oBtnReg = document.getElementById('reg_btn');
var oBtnLogin = document.getElementById('login_btn');
// 登录
oBtnLogin.onclick = function(){
ajax({
url:'/user',
data:{act:'login',user:oTxtUser.value,pass:oTxtPass.value},
type:'get',
success:function(str){
var json = eval('('+str+')');
if(json.ok){
alert('登录成功');
}else{
alert('登录失败:'+json.msg);
}
},
error:function(){
alert('通信错误');
}
})
}
// 注册
oBtnReg.onclick=function(){
ajax({
url:'/user',
data:{act:'reg',user:oTxtUser.value,pass:oTxtPass.value},
type:'get',
success:function(str){
var json = eval('('+str+')');
if(json.ok){
alert('注册成功');
}else{
alert('注册失败:'+json.msg);
}
},
error:function(){
alert('通信错误');
}
})
}
}
</script>
</head>
<body>
用户:<input type="text" id="user"><br>
密码:<input type="password" id="pass"><br>
<input type="button" value="注册" id="reg_btn">
<input type="button" value="登录" id="login_btn">
</body>
</html>
运行结果:????????????????