这即将是一篇很长很长的文章...html
// server.js const http = require('http') const server = http.createServer(() => { console.log('原地转300圈并listen3000端口') }) server.listen(3000)
首先在终端运行node server.js
使用浏览器在地址栏输入localhost:3000
会发现控制台打印以下:前端
可是此时会发现浏览器地址栏始终在加载中,而且什么也没有显示,接下来让它显示一些东西出来。node
http.createServer()
的回调函数实际上是有参数的,能够用来处理浏览器的请求,并进行响应。mysql
const server = http.createServer((req, res) => { // 处理请求和响应 console.log('原地转300圈并listen3000端口') res.write('lalala') res.end() })
此时在终端运行node server.js
使用浏览器在地址栏输入localhost:3000
会发现浏览器显示以下:git
每一次修改都要使用ctrl+c
中止服务器后再用node server.js
重启很麻烦呐...那就推荐个工具:使用npm install -g supervisor
安装supervisor,而后运行supervisor server.js
就能够实现代码更改以后页面的自动更新。
而且会发现不论地址栏如何修改,页面内容都会是这样的:github
是由于服务器并无对浏览器的请求作出响应。因此http.createServer()
回调的第一个参数就派上了用场:sql
const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') console.log(req.url) res.write('lalala') res.end() })
此时控制台输出:chrome
这里的/a/b/c.html
和/favicon.ico
都是浏览器在访问http://localhost:3000/a/b/c.html
时请求的内容,/favicon.ico
是页面标题左侧的小图标,也就是使用<link rel="shortcut icon" href="/favicon.ico">
设置的那个图标。数据库
因此就能够根据请求的url来进行相应的处理~express
const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') switch(req.url) { case '/1.html': res.write('111') break case '/2.html': res.write('222') break default: res.write('404') break } res.end() })
发现能够响应请求了是否是很棒棒!
可是每次都使用switch...case...
确定不是咱们的本意啊,因此接下来确定会进行一些懒人操做!
此次是另外一个模块的使用:fs
。
fs
模块主要有两个方法:fs.readFile()
和fs.writeFile()
、
fs.readFile('文件名', (err, data) => { if (err) { // err的处理 console.log(err) } else { // data的处理 console.log(data.toString()) } })
这里之因此要使用data.toString()
是由于readFile
的data
是buffer
类型,这个buffer
能够往后再说,当前知道使用toString()
能够转换成咱们以前输入的内容就行了。
这里若是发现出现了乱码则须要把
aaa.txt
的文件格式改为UTF-8,至于怎么改能够自行百度。
fs.writeFile('文件名', '内容', (err) => { console.log(err) })
学习了fs
的两个基本函数以后就能够来访问静态文件啦!
能够在当前目录下新建一个public
文件夹,在public
文件放一些静态文件,好比图片啊,html文件之类的。
// server.js const http = require('http') const fs = require('fs') const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') const file_name = '/public' + req.url fs.readFile(file_name, (err, data) => { if (err) { res.write('404') } else { res.write(data) } res.end() }) }) server.listen(3000)
此处注意
res.end()
的位置,readFile是异步操做,须要在回调中进行下一步的服务响应。此处返回的data不须要进行toString操做,由于浏览器能够识别buffer格式。
例如在public下放两个html文件:
<!-- 1.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> div { width: 100px; height: 100px; background-color: #f3f3f3; border: #ccc 1px solid; } </style> </head> <body> <div>11111</div> </body> </html>
<!-- 2.html,别的内容都不变,只修改body下面的 --> <body> <div>22222</div> </body>
而后对相应的路径进行访问:
啦啦啦能够直接访问文件啦!
前端请求后端数据的经常使用的两种方式:GET和POST。GET的数据是放在url中,POST的数据不在url中。因此对这两种方式,后端须要不一样的处理方式。
修改一下上节的1.html:
<!-- 别的内容都不变,只修改body下面的 --> <body> <div>11111</div> <form action="http://localhost:3000" method="get"> 用户:<input type="text" name="user" value=""><br> 密码:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form> </body>
对于GET方式,须要处理的是req.url
部分。因此能够先尝试输出:
// server.js const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') res.write(req.url) res.end() }) server.listen(3000)
点提交以后,来观察一下页面的变化:
emmmm...虽然说这确实太不安全了,可是就先学习一下思路嘛...
对于/?user=user&pass=123
,问号前面的部分是路径/
,问号后面的就是添加在url后面的参数啦,因此能够用最基本的split()
方法处理数据啊...可是要考虑那个/favicon.ico
,它没有?
没有&
,因此是要进行排雷的。
const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') var get = {} var url = req.url if (url.indexOf('?') !== -1) { var arr = url.split('?') var arr1 = arr[1].split('&') for (let i = 0; i < arr1.length; i++) { var arr2 = arr1[i].split('=') get[arr2[0]] = arr2[1] } res.write(JSON.stringify(get)) } res.end() }) server.listen(3000)
res.write()
参数只能是string或buffer,因此不能直接res.write(get)
。
此时会看到已经成功的处理了req.url
:
可是不想这么麻烦啊...因而还真的有简单的模块能够用哦!
例如:
const queryString = require('querystring') var query = queryString.parse('user=user&pass=123') console.log(query) // { user: 'user', pass: '123' }
因此就可使用这个模块来将处理方式变得简单点~
// server.js const http = require('http') const queryString = require('querystring') const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') var get = {} var url = req.url if (url.indexOf('?') !== -1) { var arr = url.split('?') get = queryString.parse(arr[1]) res.write(JSON.stringify(get)) } res.end() }) server.listen(3000)
可是这里还须要split()
一次,因此呢,还有个更简单的模块~
const urlLib = require('url') var url = urlLib.parse('localhost:4000/?user=user&pass=123') console.log(url) // Url { // protocol: 'localhost:', // slashes: null, // auth: null, // host: '4000', // port: null, // hostname: '4000', // hash: null, // search: '?user=user&pass=123', // query: 'user=user&pass=123', // pathname: '/', // path: '/?user=user&pass=123', // href: 'localhost:4000/?user=user&pass=123' }
主要看的就是Url.query
,可是如今并无根据&
切开怎么办呢,这时须要给urlLib.parse()
设置它的第二个参数为true
,说明要解析query,以下:
const urlLib = require('url') var url = urlLib.parse('localhost:4000/?user=user&pass=123', true) console.log(url.query)//{ user: 'user', pass: '123' }
// server.js const http = require('http') const urlLib = require('url') const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') var obj = urlLib.parse(url, true) var get = obj.query var url = obj.pathname res.write(JSON.stringify(get)) res.end() }) server.listen(3000)
上面的一路看过来已经get到点了...因此来post一下。
先修改以前的1.html,将get方式改成post便可
<form action="http:localhost:3000" method="post"> 用户:<input type="text" name="user" value=""><br> 密码:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form>
由于post的数据是放在HTTP报文主体中的,数据获取就是一个很大的问题,因此首先看一下node怎么接收post数据。
node接收post数据主要是使用req
的事件处理:
req.on('data', (data) => {}) // 每次接收数据时触发 req.on('end', () => {}) // 接收完毕数据时触发
由于post数据能够很大,因此对较大的数据包会进行分块再处理。分块能够避免因为种种错误而产生的大块数据从新传输,会浪费资源。
const server = http.createServer((req, res) => { console.log('原地转300圈并listen3000端口') var post = '' req.on('data', (data) => { post += data }) req.on('end', () => { res.write(JSON.stringify(post)) res.end() }) }) server.listen(3000)
能够看到显示的数据是这样的;
之因此要监听两个事件是由于POST数据较大时数据也是分块发送的,因此读者朋友能够试一下增长个<texearea>
而后多输入一些数据,好比设置个递增的变量来感觉一下data
事件的触发状况。
这里使用字符串拼接的方式来处理数据确实有一些low了,好比传输的是文件格式那就不能在字符串里面放了啊,可是先这样,目的是了解POST传输的特色啦~
对上图中的数据的处理方式还记得不?固然是牛逼哄哄的queryString
啦!对数据进行如下方式的处理,就会获得json格式的数据:
const queryString = require('querystring') //... req.on('end', () => { var POST = queryString.parse(post) res.write(JSON.stringify(POST)) res.end() })
呐呐...来应用一下噻,好比说制做一个注册登陆的界面。目前由于没有应用到数据库,就能够直接使用存储几个帐号密码的map来模拟一下~
具体代码能够看reg&login。
接下来是express的部分~
使用以前应该先安装npm install express
。
而后server.js
内容以下:
// server.js const express = require('express') const server = express() server.listen(3000)
emmmm...这个时候使用supervisor server.js
而后打开localhost:3000
会发现出现了Cannot GET /
,意思就是没什么东西嘛...那接下来就让他显示一些东西出来。
在server.listen(3000)
前加这样的代码:
server.use('/', (req, res) => { res.send('这是首页') res.end() })
此时就会发现首页上有东西啦!
express中的req
和res
实际上是进行封装过的,和原生node回调中的req
和res
兼容,这里的res.send
彻底能够改为res.write
,只是res.write
的参数必须是buffer
或string
类型,而res.send
则没有这个限制。
发现了吗,express使用的server.use('/', callback)
代替了原来对req.url
的复杂处理,不用进行parse
再switch...case
了,方便了很多~
接下来看express怎么处理用户请求~
html文件仍是以前的1.html,先<form>
元素的method
设置为get,随后再设置为post,来观察提交后的状况。server.js中更改以下:
const server = express() server.get('/', (req, res) => { res.send('get到了') res.end() }) server.post('/', (req, res) => { res.send('post到了') res.end() }) server.listen(3000)
而使用server.use
的时候则是不管哪一种请求都会触发,你们能够本身试试。
在express中,读取静态文件的方法是使用express.static设置一个放置静态文件的目录。
server.use(express.static('public'))
有了上面一句以后就能够访问public
目录下的静态页面1.html
啦~以下:
在express中处理url中的参数也有简单的方法:req.query
,若代码以下:
server.get('/', (req, res) => { res.send(req.query) res.end() })
就能够像下图同样:
因此具有了用express搞注册登陆的基础知识,能够来一波实践哦~
代码能够见2-reg-login
上文学习了express使用req.query
能够获取到GET方式的请求内容,此次来学习怎么接收POST请求并处理数据。
这里你们能够先去看看express的中间件的概念,上文中出现的express.static()
其实就是一个托管静态文件的中间件。
这里就须要用到一个叫作body-parser
的中间件,可是这个中间件就不是express内置的了,使用前须要npm install body-parser
。
使用方法很简单,仍是使用以前的1.html
,将form元素的method改成post:
<form action="http://localhost:3000/" method="post"> 用户:<input type="text" name="user" value=""><br> 密码:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form>
server.js
代码以下:
const bodyParser = require('body-parser') server.use(bodyParser.urlencoded()) server.use('/', (req, res) => { res.send(req.body) // 使用了bodyParser以后body属性上才会有值 res.end() })
而后打开1.html
输入数据后,结果以下:
因而会发现控制台会有这样一段提示:
这里的extended若是值为true则是开启扩展模式,还有另外一个参数是limit表示限制大小,默认100k。可是通常用不到扩展模式,能够直接设置为false,以下:
server.use(bodyParser.urlencoded({ extended: false, limit: 2 * 1024 * 1024 // 限制2M }))
这个时候就会发现上图中的提示没有了。
server.use('/', (req, res, next) => { console.log('a') // next() }) server.use('/', (req, res) => { console.log('b') })
如上述代码,当没有next时控制台只打印一个a,可是有了next以后,控制权能够交给下一个server.use('/', () => {})
,因此能够看到控制台打印了a和b。
server.use
参数为函数时,是对全部的请求都做用到。如上面的body-parser
。
server.use((req, res, next) => { req.on('data', ) }) server.use('/', (req, res, next) => { res.send(req.body) // 下方能够获取到 })
运行后如图:
那么就能够模仿body-parser
来搞一个中间件~
server.use((req, res, next) => { var str = '' req.on('data', (data) => { str += data }) req.on('end', () => { req.body = str next() // 注意这里next的位置 }) }) server.use('/', (req, res, next) => { res.send(req.body) })
还用以前那个1.html,结果以下图:
这时候想处理成json的话就能够用以前的querystring的parse。
呐呐此次要了解的是cookie和session~
能够在chrome浏览器F12 -> Application -> Cookies看到网页的cookie,可使用dcument.cookie
获取或修改当前页面的cookie。
server.use('/', (req, res) => { res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600 // 设置时间 }) res.send() })
此时查看浏览器控制台就会发现有了这样一个cookie:
不设置maxAge的话cookie会在浏览器关闭后失效。
也能够在res.cookie()
中为cookie设置路径以下:
server.use('/', (req, res) => { res.cookie('user', 'oneday', { path: '/one', // 设置路径 }) res.send() })
若是使用path属性设置了/one
的话,那么当访问localhost:3000
时没法读取到cookie,只有在localhost:3000/one
下的路径均可以访问到该cookie,如localhost:3000/one/a.html
。
在express中读取cookie则须要一个中间件cookie-parser
。
const cookieParser = require('cookie-parser') server.use(cookieParser()) server.use('/', (req, res) => { res.cookie('user', 'oneday', { path: '/one', maxAge: 24 * 1000 * 3600 }) res.send(req.cookies) // 使用了cookie-parser中间件后才有的cookies属性 })
这时若是访问localhost:3000
是不会看到有输出的,缘由同上,可是/one
下的路径就会有,以下图:
server.use(cookieParser()) server.use('/', (req, res) => { req.secret = '愿全部的爱和付出都不会被辜负' // 签名密钥 res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600, signed: true }) res.send(req.cookies) })
能够看到以下图的输出:
能够看到该签了名的cookie很明显的将原始的cookie包含在了内容中,emmmm内容仍是能够看到的。可是一旦cookie被修改了就能看得出来啊,因此签名只是能作到防篡改,不能作到直接加密。
签过名的cookie以s
开头,签名后的cookie长度较长,所以经cookie-parser
处理过的带签名的cookie会放在req.signedCookies
中,未签过名的就放在req.cookies
中。
// 告诉cookieParser加密使用的字符串 server.use(cookieParser('愿全部的爱和付出都不会被辜负')) server.use('/', (req, res) => { req.secret = '愿全部的爱和付出都不会被辜负' // 签名密钥 res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600, signed: true }) res.send(req.signedCookies) // 就能够将签过名的cookie原样输出 })
删除cookie则是使用res.clearCookie(name)
就能够了。
想处理session则须要一个叫cookie-session的中间件。可是要记得在使用cookie-session中间件以前先使用cookie-parser中间件。由于不处理cookie哪来的session呢~
将server.js
代码改为以下所示:
const cookieParser = require('cookie-parser') const cookieSession = require('cookie-session') server.use(cookieParser()) server.use(cookieSession()) server.use('/', (req, res) => { res.send(req.session) })
会发现报错了~~
呐呐来介绍一下这个keys。它的存在是为了预防session劫持的发生,keys是一个密钥数组,能够用来加密,express会循环使用密钥来加密cookie。
server.use(cookieSession({ keys: ['one', 'day', 'oneday'] // keys加密数组,注意使用方法 })) server.use('/', (req, res) => { if (req.session['count'] == null) { req.session['count'] = 1 } else { req.session['count']++ } console.log(req.session['count']) res.send('OK') })
会发现每次控制台的session都会刷出来两个数字,那是由于也访问到了/favicon.ico
,这个能够暂时不用管。
打开浏览器后能够看到cookie的地方有两个:
其中的session
表明的是访问次数,session.sig
表明的是签名后的session
,刷新会发现session
变化不大可是session.sig
则会发生很大的变化,这样就能够防止别人拿着上次的session假冒用户进行操做了。
session也能够进行一些自行的设置~
server.use(cookieSession({ name: 'ses', keys: ['one', 'day', 'oneday'], maxAge: 2 * 3600 * 1000 // 2小时 }))
服务器端使用delete req.session[name]
删除session。
ejs相对来讲是温和性的,能够和html共存的一个模板引擎,我的比较喜欢ejs,因此这里不介绍jade的相关知识...你们能够本身找教程~
首先固然是要使用npm install ejs
下载一下~
<!-- index.ejs --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> 个人名字叫:<%= name %> </body> </html>
// ciews下ejs.js const ejs = require('ejs') ejs.renderFile("./views/index.ejs", {name: "oneday"}, (err, data) => { if(err) { console.log("编译失败") } else { console.log(data) } })
使用node ejs.js
以后,能够获得控制台的以下输出:
<%= name %>
<% for (var i = 0; i < 3; i++) { %> <div><%= arr[i].name %></div> <% } %>
<%- <div></div> %>
<% include filepath+filename %>
,这里的filepath+filename
不能是变量。emmmm能够看express的文档模板引擎。主要就是两句代码:
server.set('views', './views') server.set('view engine', 'ejs') server.get('/index', (req, res) => { res.render('index.ejs', {name: 'oneday'}) })
这节主要讲怎么用express实现一个文件上传功能。
将1.html内容进行更改:
<form action="http://localhost:3000/" method="post"> 文件:<input type="file" name="f1"><br> <input type="submit" value="上传"> </form>
const express = require('express') const bodyParser = require('body-parser') const server = express() server.use(bodyParser.urlencoded({extended: false})) server.post('/', (req, res) => { res.send(req.body) }) server.listen(3000)
此时运行一下试试会发现只打印出来了图片的名称,意思是只上传了文件名。缘由是body-parser
只能处理表单中enctype为application/x-www-form-urlencoded
的项,上传文件须要将enctype改成multipart/form-data
,且不能用body-parser
处理了。这时就须要引入一个新的中间件:multer
。
npm install multer
以后,能够按以下操做:
<form action="http://localhost:3000/" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="f1"><br> <input type="submit" value="上传"> </form>
const multer = require('multer') var objmulter = multer() const server = express() server.use(objmulter.any()) // 能够是single(指定的名称) server.post('/', (req, res) => { res.send(req.files) })
这时再运行就会发现有不少buffers数据出来了...可是只要这样的数据确定是没有什么用处的啊,毕竟咱们要的是上传的文件嘛...那就能够像下面这样对数据进行一下处理:
var objmulter = multer({dest: './www/upload'}) // 只改这一行代码,dest指定目标文件夹
这是再运行会发现www/upload下面真的会有一个文件,可是是一个很长名字且没有后缀,也就是没有文件类型的一个文件,接下来咱们要作的就是给它加一个文件扩展名,要用到一个叫path
的包。
const path = require('path') var str = "c:\\www\\aaa\\nbb.png" var obj = path.parse(str) console.log(obj)
会发现输出以下:
因此经过如下代码能够完成一个文件上传的小应用~
const multer = require('multer') const pathLib = require('path') const fs = require('fs') var objmulter = multer({dest: './www/upload'}) const server = express() server.use(objmulter.any()) server.post('/', (req, res) => { var newName = req.files[0].path + pathLib.parse(req.files[0].originalname).ext // 重命名临时文件 fs.rename(req.files[0].path, newName, (err) => { // fs.rename if (err) { res.send('上传失败') } else { res.send('上传成功') } }) })
选择文件并上传后,会发如今www/upload下是真的有该文件的,并且能够正常打开~
这里用到的数据库是mysql,管理工具是Navicat Premium。在node中须要首先npm install mysql
,随后先进行数据库链接:
const mysql = require('mysql') const db = mysql.createConnection({ host: 'localhost', // 目标 user: 'root', // 用户名 port: 3306, // 端口号 password: '123456', // 密码 database: 'user_table' // 数据库名 })
数据库操做语法统一格式为:db.query('操做语句', (err, data) => {// callback})
SQL语句标准写法要求:一、关键字要求大写;二、库名、表名、字段名须要加上``
记录一些简单操做语句:
-- INSERT INTO 表(字段列表) VALUES(值列表) INSERT INTO `user_table` (`username`, `password`) VALUES ('one', '123456')
-- DELETE FROM 表名 (WHERE 条件) DELETE FROM `user_table` WHERE `username`='one'
-- UPDATE 表名 SET 字段=值, 字段=值, 字段=值...(WHERE 条件) UPDATE `user_table` SET `username`='oneday', `password`='233333' WHERE `username`='one'
-- SELELT (内容) FROM 表名 (WHERE 条件) SELECT * FROM `user_table`
WHERE `age` >= 18 WHERE `age` >= 18 AND `score` < 60 WHERE `cash` > 100 OR `score` > 1000
-- ASC -> 升序 | DESC -> 降序 ORDER BY `age` ASC/DESC -- 按价格升序,随后再按销量降序,多条件排序就用逗号分隔 ORDER BY `price` ASC, `sales` DESC
-- 按班级计数 SELECT `class`, COUNT(class) FROM `student_table` GROUP BY `class` -- 每一个班级的平均分 SELECT `class`, AVG(score) FROM `student_table` GROUP BY `class` -- 每一个班级的最高分和最低分 SELECT `class`, MAX(score), MIN(score) FROM `student_table` GROUP BY `class`