快速开始
- 新建一个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
复制代码
- http:localhost:3000 点击页面查看
路由
原生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点击页面查看
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('成功启动')
})
复制代码
数据请求
get数据请求
- 从上下文请求
- 从上下文的request中请求
运行
node app.js
复制代码
http://localhost:3000/page/user?a=1&b=2点击查看
格式化后的json数据
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-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实现静态资源服务器
//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中间件
// 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
复制代码
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
复制代码
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
复制代码
文件上传
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
}
复制代码
异步上传图片实现
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END