node入门

这即将是一篇很长很长的文章...html

从一个简单的服务器开始

// server.js
const http = require('http')

const server = http.createServer(() => {
    console.log('原地转300圈并listen3000端口')
})

server.listen(3000)

首先在终端运行node server.js使用浏览器在地址栏输入localhost:3000会发现控制台打印以下:前端

最简单的server

可是此时会发现浏览器地址栏始终在加载中,而且什么也没有显示,接下来让它显示一些东西出来。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

最简单的有了内容的server

每一次修改都要使用 ctrl+c中止服务器后再用 node server.js重启很麻烦呐...那就推荐个工具:使用 npm install -g supervisor安装supervisor,而后运行 supervisor server.js就能够实现代码更改以后页面的自动更新。

而且会发现不论地址栏如何修改,页面内容都会是这样的:github

最简单的内容始终同样的server

是由于服务器并无对浏览器的请求作出响应。因此http.createServer()回调的第一个参数就派上了用场:sql

const server = http.createServer((req, res) => {
    console.log('原地转300圈并listen3000端口')

    console.log(req.url)
    res.write('lalala')
    res.end()
})

此时控制台输出:chrome

可获取请求路径的server

这里的/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()
})

可响应请求路径的server

可响应请求路径的server(返回404)

发现能够响应请求了是否是很棒棒!

可是每次都使用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()是由于readFiledatabuffer类型,这个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>

而后对相应的路径进行访问:

可响应静态文件请求的server

啦啦啦能够直接访问文件啦!

这是和前端联系很紧密的一节

前端请求后端数据的经常使用的两种方式: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方式的表单

GET

对于GET方式,须要处理的是req.url部分。因此能够先尝试输出:

// server.js
const server = http.createServer((req, res) => {
    console.log('原地转300圈并listen3000端口')

    res.write(req.url)
    res.end()
})
server.listen(3000)

点提交以后,来观察一下页面的变化:

一个很low的get方式的表单

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

一个打印出密码的get方式的表单

可是不想这么麻烦啊...因而还真的有简单的模块能够用哦!

例如:

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)

POST

上面的一路看过来已经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方式的表单

之因此要监听两个事件是由于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的部分~

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启动的简易首页

express中的reqres实际上是进行封装过的,和原生node回调中的reqres兼容,这里的res.send彻底能够改为res.write,只是res.write的参数必须是bufferstring类型,而res.send则没有这个限制。

发现了吗,express使用的server.use('/', callback)代替了原来对req.url的复杂处理,不用进行parseswitch...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)

一个简易的get请求的express

而使用server.use的时候则是不管哪一种请求都会触发,你们能够本身试试。

在express中,读取静态文件的方法是使用express.static设置一个放置静态文件的目录。

server.use(express.static('public'))

有了上面一句以后就能够访问public目录下的静态页面1.html啦~以下:

一个能够访问到静态页面的express

在express中处理url中的参数也有简单的方法:req.query,若代码以下:

server.get('/', (req, res) => {
    res.send(req.query)
    res.end()
})

就能够像下图同样:

一个打印出了密码的简陋express

因此具有了用express搞注册登陆的基础知识,能够来一波实践哦~

express实现注册登陆

代码能够见2-reg-login

express处理POST请求

上文学习了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输入数据后,结果以下:

一个能够显示post数据的express

因而会发现控制台会有这样一段提示:

body-parser须要的extended提示

这里的extended若是值为true则是开启扩展模式,还有另外一个参数是limit表示限制大小,默认100k。可是通常用不到扩展模式,能够直接设置为false,以下:

server.use(bodyParser.urlencoded({
    extended: false,   
    limit: 2 * 1024 * 1024      // 限制2M
}))

这个时候就会发现上图中的提示没有了。

express的一些操做

链式操做

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) // 下方能够获取到
})

运行后如图:

自行添加的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~

cookie设置和读取

能够在chrome浏览器F12 -> Application -> Cookies看到网页的cookie,可使用dcument.cookie获取或修改当前页面的cookie。

server.use('/', (req, res) => {
    res.cookie('user', 'oneday', {
        maxAge: 24 * 1000 * 3600 // 设置时间
    })
    res.send()
})

此时查看浏览器控制台就会发现有了这样一个cookie:

本身设置的1天后过时的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下的路径就会有,以下图:

一个显示了cookie的express

cookie加密

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很明显的将原始的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

删除cookie

删除cookie则是使用res.clearCookie(name)就能够了。

session的设置和读取

想处理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)
})

会发现报错了~~

一个报了错的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的地方有两个:

有两个cookie

其中的session表明的是访问次数,session.sig表明的是签名后的session,刷新会发现session变化不大可是session.sig则会发生很大的变化,这样就能够防止别人拿着上次的session假冒用户进行操做了。

session也能够进行一些自行的设置~

server.use(cookieSession({
    name: 'ses',
    keys: ['one', 'day', 'oneday'],
    maxAge: 2 * 3600 * 1000 // 2小时
}))

session的删除

服务器端使用delete req.session[name]删除session。

ejs模板引擎

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以后,能够获得控制台的以下输出:

第一次使用ejs

ejs中的某些语法(空格不是必须的):

  • 输出变量值:<%= name %>
  • js语句:
<% for (var i = 0; i < 3; i++) { %>
    <div><%= arr[i].name %></div>
<% } %>
  • 不转义的字符串:<%- <div></div> %>
  • 引用别的文件:<% include filepath+filename %>,这里的filepath+filename不能是变量。

ejs与express配合

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)

会发现输出以下:

一个处理了路径的path包

因此经过如下代码能够完成一个文件上传的小应用~

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
-- 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'
  • 查-SELECT
-- SELELT (内容) FROM 表名 (WHERE 条件)
SELECT * FROM `user_table`

一些子句

  • where
WHERE `age` >= 18
WHERE `age` >= 18 AND `score` < 60
WHERE `cash` > 100 OR `score` > 1000
  • order
-- ASC -> 升序 | DESC -> 降序
ORDER BY `age` ASC/DESC
-- 按价格升序,随后再按销量降序,多条件排序就用逗号分隔
ORDER BY `price` ASC, `sales` DESC
  • group
-- 按班级计数
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`
相关文章
相关标签/搜索