koa-static
koa-static
Can handle static resources , Parameter is static resource folder path , The official implementation contains more parameters , You can see
koajs/static
Implementation analysis :
- Get request url route , Find out if the path under the static folder can match
- If it's a path, it's a folder , Look under the folder
index.html
file - Set response header file type (mime)
gzip
Compress , Set the compression type and return the readable stream- Error or file does not exist
next
Move on to the next middleware
const fs = require("fs")
const path = require("path")
const mime = require("mime")
const zlib = require("zlib")
function static(dir) {
return async (ctx, next) => {
try {
let reqUrl = ctx.path
let abspath = path.join(dir, reqUrl)
let statObj = fs.statSync(abspath)
// If it's a folder , Splicing index.html
if (statObj.isDirectory()) {
abspath = path.join(abspath, "index.html")
}
// Determine whether the path is accurate
fs.accessSync(abspath);
// Set file type
ctx.set("Content-Type", mime.getType(abspath) + ";charset=utf8")
// Client allowed encoding format , To determine whether or not gzip Compress
const encoding = ctx.get("accept-encoding")
if (/\bgzip\b/.test(encoding)) {
ctx.set("Content-Encoding", "gzip")
ctx.body = fs.createReadStream(abspath).pipe(zlib.createGzip())
} else if (/\bdeflate\b/.test(encoding)) {
ctx.set("Content-Encoding", "bdeflate")
ctx.body = fs.createReadStream(abspath).pipe(zlib.createDeflate())
} else {
ctx.body = fs.createReadStream(abspath)
}
} catch (error) {
await next()
}
}
}
module.exports = static
koa-bodyparser
koa-bodyparser
Can handle POST Requested data , take form-data
Data analysis to ctx.request.body
, Official address :koa-bodyparser
Implementation analysis :
- Read request data
- Set response header type
application/json
- Determine the client request header
content-type
type - Parse data and bind to
ctx.request.body
function bodyParser() {
return async (ctx, next) => {
await new Promise((resolve, reject) => {
let data = []
ctx.req.on("data", (chunk) => {
data.push(chunk)
})
ctx.req.on("end", () => {
let ct = ctx.get("content-type")
let body = {}
ctx.set("Content-Type", "application/json")
if (ct === "application/x-www-form-urlencoded") {
body = require("querystring").parse(Buffer.concat(data).toString())
}
if (ct === "application/json") {
body = JSON.parse(Buffer.concat(data).toString())
}
ctx.request.body = body
resolve()
})
ctx.req.on("error", (error) => {
reject(error)
})
})
await next()
}
}
module.exports = bodyParser
koa-router
koa-router
It can make koa image express Control routing as well , Source code koa-router The implementation is more complex , Here's a simple version .
Implementation analysis :
- Save the request to the array first
middlewares
in routes
Return middleware function , obtainctx
andnext
- From an array
middlewares
Filter outroute / Method
Same data - Middleware processing , Pass in
ctx
And packagednext
, When the processing is finished, the realnext
Method
class Router {
constructor() {
// Save an array of middleware methods
this.middlewares = []
}
get(path, handler) {
// get、post、delete、put All these methods have to deal with , Here we only realize get
this.middlewares.push({ path, handler })
}
compose(routes, ctx,next) {
// Dispatch every stored middleware method
const dispatch = (index) => {
// Execute context only after processing is completed next
if (routes.length === index) return next();
// Will middleware's next Packaged for next execution dispath, Prevent multiple execution of context next Method
routes[index].handler(ctx, () => dispatch(++index))
}
dispatch(0)
}
routes() {
return async (ctx, next) => {
// Filter out the same storage objects
let routes = this.middlewares.filter((item) => item.path === ctx.url)
this.compose(routes, ctx,next)
}
}
}
module.exports = Router;
koa-better-body
koa It's used to process file uploads koa-better-body
, Here you need to make sure that the form contains multipart/form-data
:
<form action="/submiturl" method="POST" enctype="multipart/form-data">
Here's through the form enctype by multipart/form-data
Submit the data from the background :
------WebKitFormBoundaryfCunWPksjjur83I5
Content-Disposition: form-data; name="username"
chenwl
------WebKitFormBoundaryfCunWPksjjur83I5
Content-Disposition: form-data; name="password"
1234567
------WebKitFormBoundaryfCunWPksjjur83I5
Content-Disposition: form-data; name="avatar"; filename="test.txt"
Content-Type: text/plain
Here is the content of the document
------WebKitFormBoundaryfCunWPksjjur83I5--
Implementation analysis :
- Get request information , The request header needs to have
multipart/form-data
- Cutting request information , Extract useful information
- contain
filename
Is a file , Write to the corresponding path - Extract other information and save it to
ctx.request.fields
const fs = require("fs");
const path = require("path");
Buffer.prototype.split = function(sep){
let arr = [];
let offset = 0;
let len = Buffer.from(sep).length;
let current = this.indexOf(sep,offset);
while (current !== -1) {
let data=this.slice(offset,current)
arr.push(data);
offset = current+len;
current = this.indexOf(sep, offset)
}
arr.push(this.slice(offset));
return arr;
}
module.exports = function ({ uploadDir }) {
return async (ctx, next) => {
// The result is put in req.request.fields
await new Promise((resolve, reject) => {
let data = []
ctx.req.on("data", (chunk) => {
data.push(chunk)
})
ctx.req.on("end", () => {
// multipart/form-data; boundary=----WebKitFormBoundaryvFyQ9QW1McYTqHkp
const contentType = ctx.get("content-type")
if (contentType.includes("multipart/form-data")) {
const boundary = "--"+contentType.split("=")[1];
const r = Buffer.concat(data);
const arr = r.split(boundary).slice(1,-1);
const fields = {};
arr.forEach(line=>{
let [head,body] = line.split("\r\n\r\n");
body = body.slice(0,-2); // Take out the valid content
head = head.toString();
if(head.includes("filename")){
// Processing documents
// Request header length = Total content length - Head length - 4 Newline length
const filecontent = line.slice(head.length+4,-2);
const filenanme = head.match(/filename="(.*?)"/)[1] || uid();
const uploadPath = path.join(uploadDir, filenanme)
fs.writeFileSync(uploadPath, filecontent)
}else{
fields[head.match(/name="(.*?)"/)[1]] = body.toString();
}
})
ctx.request.fields = fields
}
resolve()
})
ctx.req.on("error", (error) => {
reject(error)
})
})
await next()
}
}
function uid(){
return Math.random().toString(32).slice(2);
}