一文解决koa后端开发
Author:zhoulujun Date:
首先在mac上搭建环境:mac搭建php(mysql)运行环境—搭建xmapp
之前一直都是使用php,现在mysql 用nodejs 玩耍,更加舒服
搭建koa2
安装koa2 koa-bodyparser koa-router koa2-cors koa-views jsonwebtoken
npm i koa-bodyparser koa-body koa-router koa-views koa-static koa2-cors jsonwebtoken --save
koa-bodyparser:用于接收并解析前台发送过来的post数据
koa-body 允许上传文件
koa-router:路由
koa2-cors:用来解决前端的跨域
koa-views 解析前端hmtl 模板文件
koa-static 静态资源服务
jsonwebtoken token登录验证
koa-jwt 中间件 对 jsonwebtoken 进一步的封装,主要用来校验 token
下面的代码是验证koa是否搭建成功
const Koa = require('koa'); const app = new Koa(); app.use(async (ctx) => { ctx.body = 'hello'; }) app.listen(3000);
这是为了启动一个后台,可以用:https://www.npmjs.com/package/koa-generator
koa静态资源服务器
const Koa = require('koa') const views = require('koa-views'); const static = require('koa-static'); let app = new Koa(); app.use(views(__dirname + '/views')) // 1.静态资源服务,指定对外提供访问的根目录,不包括 public app.use(static(__dirname + '/public')); app.listen(3000);
比如图片文件在pulic/upload/c65247fc3be64e100de570900.jpg
实际访问路径是:
koa路由配置
/router/index
let api = require("./api"); module.exports = (app)=>{ app.use(api.routes()) }
路由详细配置
const Router = require('koa-router') const newsRouter = new Router({ prefix: '/api' //前缀 }) const Controller = require('../controller/api') newsRouter.post('/login', Controller.login) newsRouter.get('/product', Controller.getProduct) newsRouter.post('/product', Controller.addProduct) module.exports = newsRouter
koa 控制器配置
const mysql = require('../mysql'); const addToken = require('../token/index'); module.exports = { async getProduct(ctx) { let data = { a: 5 } ctx.body = { 'result': true, 'data': data, 'message': 'ok' } }, async login(ctx) { let { user, password } = ctx.request.body if(!user||!password){ return ctx.body = { 'result': false, 'data': data, 'message': '参数不合法' } } console.log(user, password) let sql = `select id, user from user where \`user\` = "${user}" AND \`password\` = "${password}";` let data = await mysql.query(sql) if (data?.length===1) { const user = data[0] let token = addtoken(user) //token中要携带的信息,自己定义 ctx.body = { 'result': true, 'data': {user,token}, 'message': 'ok' } }else { ctx.body = { 'result': false, 'data': data, 'message': '用户活密码不正确,无法登录' } } }, async addProduct(ctx) { const file = ctx.request.files.file; const basename = path.basename(file.path); //这时拿到的 localhost:端口号/uploads/ 文件名 //但是不可能把这串地址写死进去, 因为部署到服务器要添加域名 //用灵活变量代表 ctx.body = { url: `${ctx.origin}/uploads/${basename}` } //ctx.origin 就是 localhost:端口号 } }
koa mysql连接配置:
mysql配置文件:
// config/defualt.js // 设置配置文件 const config = { // 启动端口 port: 3000, // 数据库配置 database: { DATABASE: 'taihe', USERNAME: 'root', PASSWORD: '123456', PORT: '3306', HOST: 'localhost' } } module.exports = config
koa mysql查询器
const mysql = require('mysql'); const config = require('../config/defualt') const pool = mysql.createPool({ host : config.database.HOST, user : config.database.USERNAME, password : config.database.PASSWORD, database : config.database.DATABASE }); class Mysql { constructor () { } query (sql='SELECT * from user') { return new Promise((resolve, reject) => { pool.query(sql, function (error, results, fields) { if (error) { throw error }; resolve(results) // console.log('The solution is: ', results[0].solution); }); }) } } module.exports = new Mysql()
下面是正式代码
// index.js const Koa = require('koa') const views = require('koa-views'); const static = require('koa-static'); const koaBody = require('koa-body'); const cors = require('koa2-cors'); const config = require('./config/defualt') const mysql = require('./mysql/index') const path = require('path') const Router = require('@koa/router'); let router = require('./router/router'); const PORT = 3005 let app = new Koa(); //这是处理前端跨域的配置 app.use(cors( { /* origin: function (ctx) { // if (ctx.url === '/login') { // return "*"; // 允许来自所有域名请求 // } return '*'; },*/ exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, allowMethods: ['GET', 'POST', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization', 'Accept'], } )); app.use(koaBody({ multipart: true, //允许上传文件 formidable: { //这是个 node 包, 设置一下选项 uploadDir: path.join(__dirname, '/static/uploads'), //设置上传目录 keepExtensions: true, //设置文件后缀名保留 } })) app.use(views(__dirname + '../dist')) app.use(static(__dirname + '../static/')); router(app); app.listen(PORT) app.use(async (ctx) => { let data = await mysql.query() ctx.body = { 'result': true, 'data': data, 'message': 'ok' } }) app.listen(config.port) console.log(`listening on port ${config.port}`)
koa登录 token鉴权
具体参看:【Node】使用 koa2 实现一个简单JWT鉴权 https://juejin.cn/post/6921493257578872845
生成token
const jwt = require('jsonwebtoken'); const serect = 'token'; //密钥,不能丢 module.exports = (userinfo) => { //创建token并导出 return jwt.sign({ user: userinfo.user, id: userinfo.id }, serect, {expiresIn: '1h'}); };
然后再login api 里面返回token,在前端 拿到login数据
前端缓存token
login() { this.$axios .post("/api/login", { ...this.ruleForm, }) .then(res => { if (res.code === "0") { this.$message.success('登录成功'); localStorage.setItem("token", res.data.token); this.$router.push("/"); } else { this.$message(res.message); } }); }
ajax请求统一鉴权
封装 axios 的拦截器,每次请求的时候把 token 带在请求头发送给服务器进行验证。这里如果之前放在 Cookie 中,可以让它自动发送,但是这样不能跨域。所以推荐做法是放在 HTTP 请求头 Authorization 中,注意这里的 Authorization 的设置,前面要加上 Bearer 。详情可以见 Bearer Authentication
Authorization为啥必须要以Bearer开头
Bearer代表Authorization头定义的schema ,除了Bearer,还有其它的一些 schemas , 标准规范请查看文档地址:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
// axios 请求拦截器处理请求数据 axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); config.headers.common['Authorization'] = 'Bearer ' + token; // 留意这里的 Authorization return config; })
校验 token
koa解决跨域的方法
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一个与当前页面地址不同即为跨域。存在跨域的情况:
网络协议不同,如http协议访问https协议。
端口不同,如80端口访问8080端口。
域名不同,如zhoulujun.cn访问zhoulujun.cn。
子域名不同,如www.zhoulujun.cn访问cdn.zhoulujun.cn。
域名和域名对应ip,如www.zhoulujun.cn访问127.0.0.1
跨域请求资源的方法
JSONP跨域
nginx反向代理
服务器端修改heade
document.domain
window.name
postMessage
koa中如何设置跨域
自行添加一个中间件
app.use(async (ctx, next) => { // 允许来自所有域名请求 ctx.set("Access-Control-Allow-Origin", "*"); // 这样就能只允许 http://localhost:8080 这个域名的请求了 // ctx.set("Access-Control-Allow-Origin", "http://localhost:8080"); // 设置所允许的HTTP请求方法 ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE"); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段. ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type"); // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。 // Content-Type表示具体请求中的媒体类型信息 ctx.set("Content-Type", "application/json;charset=utf-8"); // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。 // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"; ctx.set("Access-Control-Allow-Credentials", true); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。 // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证 // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证 ctx.set("Access-Control-Max-Age", 300); /* CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段: Cache-Control、 Content-Language、 Content-Type、 Expires、 Last-Modified、 Pragma。 */ // 需要获取其他字段时,使用Access-Control-Expose-Headers, // getResponseHeader('myData')可以返回我们所需的值 //https://www.rails365.net/articles/cors-jin-jie-expose-headers-wu ctx.set("Access-Control-Expose-Headers", "myData"); await next(); })
使用koa2-cors
const Koa = require('koa'); const cors = require('koa2-cors'); const app = new Koa(); app.use(cors()); //或者 app.use( cors({ origin: function(ctx) { //设置允许来自指定域名请求 if (ctx.url === '/test') { return '*'; // 允许来自所有域名请求 } return 'http://localhost:8080'; //只允许http://localhost:8080这个域名的请求 }, maxAge: 5, //指定本次预检请求的有效期,单位为秒。 credentials: true, //是否允许发送Cookie allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //设置所允许的HTTP请求方法 allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段 exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段 }) );
参看文章:koa解决跨域的方法 https://juejin.cn/post/6844904042196533255
koa 图片上传详解
参考文章:
koa 图片上传详解 https://learnku.com/articles/38437
koa2基于stream(流)进行文件上传和下载 https://www.cnblogs.com/tugenhua0707/p/10828869.html
const logger = require("koa-logger") const serve = require("koa-static") const koaBody = require("koa-body") const Koa = require('koa') const fs = require("fs") const app = new Koa() const os = require("os") const path = require("path") app.use(logger()) // 使用文件上传中间件 app.use(koaBody({ multipart: true })) // 1.静态资源服务,指定对外提供访问的根目录 app.use(serve(path.join(__dirname, '/public'))); app.use(async function (ctx, next) { await next() if (ctx.body || !ctx.idempotent) return ctx.response.body = '<h1>Hello, koa2!</h1><p>not upload photo</p>' }) // 2. 文件上传 app.use(async function (ctx, next) { if ('POST' != ctx.method) return await next() // 获取图片源 // <input type="file" name="file" multiple> const file = ctx.request.files.file // 接收读出流 const reader = fs.createReadStream(file.path) // 创建写入流 // 3. 指定图片路径文件名(即上传图片存储目录) const stream = fs.createWriteStream(path.join('public/images', file.name)) // 用管道将读出流 "倒给" 输入流 reader.pipe(stream) // 4.打印上传文件在服器上存储的相对路径 console.log('uploading %s -> %s', file.name, stream.path) // 5.重定向到基于根目录下的静态资源web访问路径,展示图片 ctx.redirect(stream.path.substr(6).replace(/\\/g,'/')) }) // 监听 app.listen(3000, () => { console.log("listening port 3000"); })
其实,被这些文章坑了。用了koa-body后,直接这返回路径就可以了
module.exports = { async add(ctx) { const file = ctx.request.files.file; const fileName = file.newFilename console.log(fileName) ctx.body = { data:{ src:fileName, path:uploadUrl}, code: 0, message: '上传成功' }; } }
koa-bodyparser
如果要处理post请求,可以用 router.post('/path', async fn)。
用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!
所以,我们需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到 ctx.request.body 中。
koa-bodyparser就是用来干这个活的。
注意,由于middleware的顺序很重要,这个 koa-bodyparser 必须在router之前被注册到app对象上。如果 ctx.request.body 为 undefined,说明缺少middleware,或者middleware没有正确配置。
koa2 使用 koa-body 代替 koa-bodyparser 和 koa-multer!
具体查看:http://www.ptbird.cn/koa-body.html
koa-body 的基本参数
参数名 | 描述 | 类型 | 默认值 |
---|---|---|---|
patchNode | 将请求体打到原生 node.js 的ctx.req 中 | Boolean | false |
patchKoa | 将请求体打到 koa 的 ctx.request 中 | Boolean | true |
jsonLimit | JSON 数据体的大小限制 | String / Integer | 1mb |
formLimit | 限制表单请求体的大小 | String / Integer | 56kb |
textLimit | 限制 text body 的大小 | String / Integer | 56kb |
encoding | 表单的默认编码 | String | utf-8 |
multipart | 是否支持 multipart-formdate 的表单 | Boolean | false |
urlencoded | 是否支持 urlencoded 的表单 | Boolean | true |
text | 是否解析 text/plain 的表单 | Boolean | true |
json | 是否解析 json 请求体 | Boolean | true |
jsonStrict | 是否使用 json 严格模式,true 会只处理数组和对象 | Boolean | true |
formidable | 配置更多的关于 multipart 的选项 | Object | {} |
onError | 错误处理 | Function | function(){} |
stict | 严格模式,启用后不会解析 GET, HEAD, DELETE 请求 | Boolean | true |
formidable 的相关配置参数
参数名 | 描述 | 类型 | 默认值 |
---|---|---|---|
maxFields | 限制字段的数量 | Integer | 1000 |
maxFieldsSize | 限制字段的最大大小 | Integer | 2 * 1024 * 1024 |
uploadDir | 文件上传的文件夹 | String | os.tmpDir() |
keepExtensions | 保留原来的文件后缀 | Boolean | false |
hash | 如果要计算文件的 hash,则可以选择 md5/sha1 | String | false |
multipart | 是否支持多文件上传 | Boolean | true |
onFileBegin | 文件上传前的一些设置操作 | Function | function(name,file){} |
关于 onFileBegin 的更多信息可以查看:https://github.com/felixge/node-formidable#filebegin
Koa 本地搭建 HTTPS 环境
首先依赖于 https koa-sslify
// 引入https 以及 koa-ssl const https = require('https') const sslify = require('koa-sslify').default if (Const.dev === 'product') { const https = require('https') const sslify = require('koa-sslify').default app.use(sslify()) const options = { key: fs.readFileSync(__dirname + '/certificate/www.zhoulujun.co.key'), cert: fs.readFileSync(__dirname + '/certificate/www.zhoulujun.co_bundle.pem'), } https.createServer(options, app.callback()).listen(config.port, (err) => { if (err) { console.log('服务启动出错', err); } else { // db.connect(); // 数据库连接 console.log('guessWord-server运行在' + config.port + '端口'); } }); } else { app.listen(config.port) }
具体参看:koa2如何使用https https://blog.csdn.net/qq_30604453/article/details/100092986
kao学习文档:https://chenshenhai.github.io/koa2-note/note/project/session.html
参考文章:
【Node】使用 koa2 实现一个简单JWT鉴权 https://juejin.cn/post/6921493257578872845
vue+koa2+token登陆验证 https://segmentfault.com/a/1190000017379244
koa解决跨域的方法 https://juejin.cn/post/6844904042196533255
浅谈koa跨域问题及koa2-cors中间件 https://blog.csdn.net/qq_30868289/article/details/83657535
koa2基于stream(流)进行文件上传和下载 https://www.cnblogs.com/tugenhua0707/p/10828869.html
koa2+mysql实现简单的登陆注册 https://blog.csdn.net/itKingOne/article/details/89703097
koa2+mysql+vue实现用户注册、登录、token验证 https://www.programminghunter.com/article/6015355220/
koa2+mysql+vue实现用户注册、登录、token验证 https://www.shuzhiduo.com/A/E35pRajyzv/
vue+koa2+token登陆验证 https://segmentfault.com/a/1190000017379244
KOA 与 REST API https://www.jianshu.com/p/1a98f2b2e7bf
koa 图片上传详解 https://learnku.com/articles/38437
koa 搭建一个后台,实现crud, swagger,loger记录等 https://juejin.cn/post/6974294533030805534
转载本站文章《一文解决koa后端开发》,
请注明出处:https://www.zhoulujun.cn/html/webfront/server/koa/8800.html