koa2学习记录

快速开始

  • 新建一个koa2-demo文件夹
  • 通过webstorm打开这个文件夹
  • 安装koa
npm i koa
复制代码
  • 建立一个app.js文件
const Koa = require('koa')
const app = new Koa()

app.use( async ( ctx ) => {
    ctx.body = 'hello koa2'
})

app.listen(3000)
console.log('启动成功')
复制代码
  • 另建一个package.json
{
  "name": "koa2-demo",
  "version": "1.0.0",
  "description": "koa2 demo",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "keywords": [
    "koa",
    "async"
  ],
  "author": "zxq",
  "license": "MIT",
  "repository": {
    "type": "git"
   },
  "dependencies": {
    "koa": "2.13.1"
  }
}
复制代码

运行

node app.js
复制代码

Snipaste_2021-04-29_20-49-23.jpg

路由

原生koa实现路由

//app.js

const Koa = require('koa')
const fs = require('fs')
const app = new Koa()

// promise封装异步读取文件方法
function render(page){
    return new Promise((resolve,reject)=>{
        let viewUrl = `./view/${page}`
        //读取文件
        fs.readFile(viewUrl,"binary",(err,data)=>{
            if(err){
                reject(err)
            }else{
                resolve(data)
            }
        })
    })
}

async function route(url){
    let view = '404.html'
    switch (url){
        case '/':
            view = "index.html"
            break
        case '/index':
            view = 'index.html'
            break
        case '/todo':
            view = 'todo.html'
            break
        case '/404':
            view = '404.html'
            break
        default:
            break
    }
    let html = await render(view)
    return html
}
//app.use()将给定的中间件方法添加到此应用程序,app.use()返回this
app.use(async(ctx)=>{
    // ctx是Context
    // ctx.request是Node的request请求
    
    // 获取用户访问的路由信息
    let url = ctx.request.url
    // 通过路由信息进行对应的fs文件写入
    let html = await route(url)
    ctx.body = html
    //ctx.body 就是 ctx.response.body 响应体
})
app.listen(3000,()=>{
    console.log('启动成功')
})
//app.listen()方法是一个语法糖,是以下方法的语法糖
const http = require('http')
http.createServer(app.callback()).listen(3000)
复制代码
//index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>index</title>
</head>
<body>
<h1>koa2 demo index page</h1>
<p>this is a index page</p>
<ul>
    <li><a href="/">/</a></li>
    <li><a href="/index">/index</a></li>
    <li><a href="/todo">/todo</a></li>
    <li><a href="/404">/404</a></li>
    <li><a href="/nofund">/nofund</a></li>
</ul>
</body>
</html>
复制代码
//404.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>404</title>
</head>
<body>
<h1>koa2 demo 404 page</h1>
<p>this is a 404 page</p>
</body>
</html>
复制代码
//todo.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>todo</title>
</head>
<body>
<h1>koa2 demo todo page</h1>
<p>this is a todo page</p>
</body>
</html>
复制代码

运行

node -harmony app.js
复制代码

http://localhost:3000/index点击页面查看

Snipaste_2021-05-06_11-13-11.jpg

koa-router路由中间件

安装

npm install --save koa-router@7
复制代码
//app.js

const Koa = require('koa')
const fs = require('fs')
const app = new Koa()

//子路由1
const Router = require('koa-router')
let home = new Router()
home.get('/',async(ctx)=>{
    let html = `
    <ul>
    <li><a href="https://juejin.cn/page/helloworld">/page/helloworld</a></li>
    <li><a href="https://juejin.cn/page/404">/page/404</a></li>
    </ul>
    `
    ctx.body = html
    //可以通过编辑app.context为ctx添加其他属性
})


// 子路由2
let page = new Router()
page.get('/404',async(ctx)=>{
    ctx.body = '404 page!'
}).get('/helloworld',async(ctx)=>{
    ctx.body = "helloworld page"
})

//装载所有子路由
let router = new Router()
router.use('/',home.routes(),home.allowedMethods())
router.use('/page',page.routes(),page.allowedMethods())

//加载路由中间件
app.use(router.routes()).use(router.allowedMethods())

app.listen(3000,()=>{
    console.log('成功启动')
})
复制代码

rroute-koa.gif

数据请求

get数据请求

  • 从上下文请求
  • 从上下文的request中请求

运行

node app.js
复制代码

http://localhost:3000/page/user?a=1&b=2点击查看
Snipaste_2021-05-06_14-35-01.jpg
格式化后的json数据
Snipaste_2021-05-06_14-35-21.jpg

POST请求

//app.js
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx)=>{
    if(ctx.url === '/' && ctx.method === 'GET'){
        let html = `
        <h1>koa2 request post demo</h1>
        <form method="POST" action="/">
        <p>userName</p>
        <input name="userName"/><br/>
        <p>nickName</p>
        <input name="nickName"/><br/>
        <p>email</p>
        <input name="email" /><br/>
        <button type="submit">submit</button>
        </form>  
        `
        ctx.body = html
    }else if(ctx.url === '/'&& ctx.method === 'POST'){
        let postData = await parsePostData(ctx)
        ctx.body = postData
    }else {
        ctx.body = '<h1>404!!!</h1>'
    }
})
function parsePostData(ctx){
    return new Promise((resolve,reject)=>{
        try {
            let postdata = "";
            ctx.req.addListener('data',(data)=>{
                postdata += data
            })
            ctx.req.addListener("end",function(){
                let parseData = parseQueryStr(postdata)
                resolve(parseData)
            })
        } catch (err){
            reject(err)
        }
    })
}

function parseQueryStr(queryStr){
    let queryData = {}
    let queryStrList = queryStr.split('&')
    console.log(queryStrList);
    for(let [index,queryStr] of queryStrList.entries()){
        let itemList = queryStr.split('=')
        queryData[itemList[0]] = decodeURIComponent(itemList[1])
    }
    return queryData
}

app.listen(3000,()=>{
    console.log('成功')
})
复制代码

koa-post.gif

Snipaste_2021-05-06_15-43-36.jpg

koa-bodyparser中间件

//app.js

const Koa = require('koa')
const app = new Koa()
const bodyParser = require('koa-bodyparser')

app.use(bodyParser())

app.use(async (ctx)=>{
    if(ctx.url === '/' && ctx.method === 'GET'){
        let html = `
        <h1>koa2 request post demo</h1>
        <form method="POST" action="/">
        <p>userName</p>
        <input name="userName" /><br/>
        <p>nickName</p>
        <input name="nickName" /><br/>
        <p>email</p>
        <input name="email">
        <button type="submit">submit</button>
        </form>
        `
        ctx.body = html
    }else if(ctx.url === '/' && ctx.method === 'POST'){
        let postData = ctx.request.body
        ctx.body = postData
    }else {
        ctx.body = '<h1>404!!!</h1>'
    }
})

app.listen(3000,()=>{
    console.log('成功')
})
复制代码

koa-bodyparser.gif

静态资源加载

原生Koa实现静态资源服务器

Snipaste_2021-05-07_16-51-49.jpg

//index.js

const Koa = require('koa')
const path = require('path')
const content = require('./util/content')
const mimes = require('./util/mimes')

const app = new Koa()
// 静态资源目录的路径
const staticPath = './static'

// 解析资源类型
function parseMime(url){
    //path.extname会返回path的扩展名
    let extName = path.extname(url)
    extName = extName ? extName.slice(1): 'unknown'
    return mimes[extName]
}
app.use(async(ctx)=>{
    // __dirname当前模块的目录名
    let fullStaticPath = path.join(__dirname,staticPath)

    // 获取静态资源内容
    let _content = await content(ctx,fullStaticPath)
    // 解析内容的类型
    let _mime = parseMime(ctx.url)
    // 若有对应的文件类型,就配置上下文的类型
    if(_mime){
        ctx.type = _mime
    }
    // 若输出的静态资源是图片,
    if(_mime && _mime.indexOf('image/') >= 0 ){
        // 设置响应头
        ctx.res.writeHead(200)
        // 将静态资源内容以二进制数据形式输出
        ctx.res.write(_content,'binary')
        // 结束响应过程
        ctx.res.end()
    }else {
        // 若不是图片,则输出其他文本
        ctx.body = _content
    }
})

app.listen(3000)
console.log('成功启动')
复制代码
//content.js

const path = require('path')
const fs = require('fs')
const dir = require('./dir')
const file = require('./file')


async function content(ctx,fullStaticPath){
    // ctx koa上下文
    // 静态资源目录在本地的绝对路径

    // 将静态资源和获取请求路径连接在一起
    let reqPath = path.join(fullStaticPath,ctx.url)
    // 判断路径是否存在在目录或者文件中
    let exist = fs.existsSync(reqPath)
    let content = ''
    // 如果路径不存在,404
    if(!exist){
        content = '404 Not Found!'
    }else {
        // 如果存在,异步返回给定文件路径的信息,判断是文件还是目录
        let stat = fs.statSync(reqPath)
        // 如果是目录
        if(stat.isDirectory()){
            content = dir(ctx.url,reqPath)
        }else {
            // 若为文件,则读取文件内容
            content = await file(reqPath)
        }
    }
    return content
}
module.exports = content
复制代码
//dir.js

const url = require('url')
const fs = require('fs')
const path = require('path')

const walk = require('./walk')

function dir(url,reqPath){
    let contentList = walk(reqPath)
    let html = `<ul>`
    for(let [index,item] of contentList.entries()){
        html = `${html}<li><a href="https://juejin.cn/post/${url === '/' ? '' : url}/${item} ">${item}</a></li>`
    }
    html = `${html}</ul>`
    return html
}
module.exports = dir
复制代码
//file.js

const fs = require('fs')

function file (filePath) {
    let content = fs.readFileSync(filePath,'binary')
    return content
}
module.exports = file
复制代码
// mimes.js

let mimes = {
    'css': 'text/css',
    'less': 'text/css',
    'gif': 'image/gif',
    'html': 'text/html',
    'ico': 'image/x-ico',
    'jpeg': 'image/jpeg',
    'jpg' : 'image/jpg',
    'js': 'text/javascript',
    'json': 'application/json',
    'pdf': 'application/pdf',
    'png': 'image/png',
    'svg': 'image/svg+xml',
    'swf': 'application/x-shockwave-flash',
    'tiff': 'image/tiff',
    'txt': 'text/plain',
    'wav': 'audio/x-wav',
    'wma': 'audio/x-ms-wma',
    'wmv': 'video/x-ms-wmv',
    'xml': 'text/xml'
}
module.exports = mimes
复制代码
//walk.js

const fs = require('fs')
const mimes = require('./mimes')

function walk(reqPath){
    let files = fs.readdirSync(reqPath)
    let dirList = [],fileList = []
    for(let i = 0,len = files.length;i<len;i++){
        let item = files[i]
        let itemArr = item.split("\.")
        let itemMime = (itemArr.length > 1 )? itemArr[itemArr.length-1]:"undefined"
            if(typeof mimes[itemMime] === "undefined"){
                dirList.push(files[i])
            }else {
                fileList.push(files[i])
            }
    }
    let result = dirList.concat(fileList)
    return result
}
module.exports = walk
复制代码

运行

node index.js
复制代码

koa-static.gif

Koa-static中间件

// index.js

const Koa = require('koa')
const path = require('path')
const static = require('koa-static')

const app = new Koa()

const staticPath = './static'
app.use(static(path.join(__dirname,staticPath)))

app.use(async (ctx) => {
    ctx.body = "hello world"
})
app.listen(3000,()=>{
    console.log('成功启动')
})
复制代码
// static/index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>index</title>
    <style>
        h1 {
            color: lightblue;
        }
    </style>
</head>
<body>
<h1>hello world</h1>
<img src="image/koa2.jpg" alt="">
</body>
</html>
复制代码
  • 运行
node index.js
复制代码

Snipaste_2021-05-12_11-21-16.jpg

cookie/session

koa2使用cookie

const Koa = require('koa')
const app = new Koa()

app.use(async(ctx)=>{
    if(ctx.url === '/index'){
    // 在上下文中写入cookie
        ctx.cookies.set(
            'cid',
            'hello world',
            {
                domain: 'localhost', // 写cookie所在的域名
                path: '/index', // 写cookie所在的路径
                maxAge: 10 * 60 * 1000, // cookie有效时长
                expires: new Date('2021-05-15'),// cookie失效时长
                httpOnly: false, // 是否用于http请求获取
                overwrite: false // 是否用于重写
            }
        )
        ctx.body = 'cookie is ok' 
    }else {
        ctx.body = 'hello world' 
    }
})

app.listen(3000,()=>{
    console.log('成功启动')
    }
)
复制代码
  • 运行
node index.js
复制代码

Snipaste_2021-05-12_13-33-38.jpg
Snipaste_2021-05-12_13-34-06.jpg
Snipaste_2021-05-12_13-46-28.jpg

koa2实现session

需要mysql暂时不动

复制代码

koa加载模板引擎

ejs 是一个模板引擎,用javaScript代码生成HTML

  • view
    • index.ejs
  • index.js
  • package.json
// index.js

const Koa = require('koa')
const views = require('koa-views')
const path = require('path')
const app = new Koa()

// 加载模板引擎
app.use(views(path.join(__dirname,'./view'),{
    extension: 'ejs'
}))

app.use(async(ctx)=>{
    let title = 'hello koa2'
    await ctx.render('index',{
        title,
    })
})

app.listen(3000,()=>{
    console.log('成功启动')
})
复制代码
// view/index.ejs
<!doctype html>
<html lang="en">
<head>
    <title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<p>EJS Welcome to <%= title %></p>
</body>
</html>
复制代码

运行

node index.js
复制代码

Snipaste_2021-05-12_16-54-09.jpg

文件上传

busboy模块

  • index.js
  • package.json
  • util
    • upload.js
// index.js

const Koa = require('koa')
const path = require('path')
const app = new Koa()
const { uploadFile } = require('./util/upload')

app.use(async (ctx)=>{
    if(ctx.url === '/' && ctx.method === 'GET'){
        let html = `
        <h1>koa2 upload demo</h1>
        <form method="POST" action="/upload.json" enctype="multipart/form-data">
        <p>file upload</p>
        <span>picName:</span><input name="picName" type="text" /><br/>
        <input name="file" type="file"><br/><br/>
        <button type="submit">submit</button>
        </form>
        `
        ctx.body = html
    }else if(ctx.url === '/upload.json' && ctx.method === 'POST'){
        let result = {success: false}
        let serverFilePath = path.join(__dirname,'upload-files')

        result = await uploadFile(ctx,{
            fileType: 'album',
            path: serverFilePath
        })
        ctx.body = result
    }else {
        ctx.body = '<h1>404!!!</h1>'
    }
})

app.listen(3000,()=>{
    console.log('启动成功')
})
复制代码
//upload.js

const inspect = require('util').inspect
const path = require('path')
const os = require('os')
const fs = require('fs')
const Busboy = require('busboy')

// 创建文件目录
function mkdirsSync(dirname) {
    // 若路径存在返回true
    if(fs.existsSync(dirname)){
        return true
    }else {
        //path.dirname()获取路径中的目录名
        if(mkdirsSync(path.dirname(dirname))){
            // 创建目录
            fs.mkdirSync(dirname)
            return true
        }
    }
}

// 获取上传文件的后缀名
function getSuffixName(fileName){
    let nameList = fileName.split('.')
    return nameList[nameList.length - 1]
}

// 上传文件
function uploadFile(ctx,options){
    let req = ctx.req
    let res = ctx.res
    let busboy = new Busboy({headers:req.headers})

    // 获取类型
    let fileType = options.fileType || 'common'
    let filePath = path.join(options.path, fileType)
    let mkdirResult = mkdirsSync(filePath)

    return new Promise((resolve, reject) => {
        console.log('文件上传中。。。')
        let result = {
            success: false,
            formData: {}
        }

        // 解析请求文件事件
        busboy.on('file',function(fieldname,file,filename,encoding,mimetype){
            let fileName = Math.random().toString(16).substr(2) + '.' + getSuffixName(filename)
            let _uploadFilePath = path.join(filePath,fileName)
            let saveTo = path.join(_uploadFilePath)

            // 文件保存到指定路径
            file.pipe(fs.createWriteStream(saveTo))

            // 文件写入事件结束
            file.on('end',function(){
                result.success = true
                result.message = '文件上传成功'
                console.log('文件上传成功!')
                resolve
            })
        })
        // 解析表单中其他字段信息
        busboy.on('field',function(fieldname,val,fieldnameTruncated,valTruncates,encoding,mimety){
            console.log('表单字段数据[' + fieldname + ']:valueL' + inspect(val));
            result.formData[fieldname] = inspect(val)
        })

        // 解析结束事件
        busboy.on('finish',function(){
            console.log('文件上结束');
            resolve(result)
        })

        // 解析错误事件
        busboy.on('error',function(err){
            console.log('文件上出错');
            reject(result)
        })
        req.pipe(busboy)
    })
}
module.exports = {
    uploadFile
}
复制代码

koa2.gif

koa-upload1.jpg

Snipaste_2021-05-13_17-09-05.jpg

异步上传图片实现

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享