• koa框架(二) mvc 模式及实现一个koa框架的web服务


    mvc三层架构

    • mvc, 即 model 、controller、view;
    • mvc模式将model、view、controller分离;使用mvc分层是系统更加灵活,扩展性更强。让程序更加直观、复用性更强、可维护性更强。
      • model 负责数据访问;
      • controller 负责消息处理;
      • view 负责视图呈现。
        在这里插入图片描述

    利用mvc模式实现后端分层

    • mvc分层开发模式: web server : 用户发起请求 => 分析用户请求,处理路由 => 处理数据(操作数据库,操作缓存)=> view(data+template)=> response。即 controller => model => view。
      在这里插入图片描述

    实现一个koa框架的web服务

    • app.js
    const Koa = require('koa');
    const KoaRouter = require('koa-router');
    const KoaStaticCache = require('koa-static-cache');
    const koaBody = require('koa-body');
    // 控制器加载
    const mainController = require('./contollers/main');
    const userController = require('./contollers/user');
    const itemController = require('./contollers/item');
    const server = new Koa();
    const router = new KoaRouter();
    // 静态资源处理
    server.use( KoaStaticCache('./public', {
        prefix: '/public',
        gzip: true,
        dynamic: true
    }) );
    // body 解析中间件
    server.use( koaBody({
        multipart: true,
        // 处理上传的二进制文件
        formidable: {
            // 上传目录
            uploadDir: __dirname + '/public/upload',
            // 是否保留上传文件名后缀
            keepExtensions: true
        }
    }));
    router.get('/', mainController.index);
    router.get('/user/register', userController.register);
    router.get('/user/login', userController.login);
    router.get('/item/add', itemController.add);
    router.post('/item/add', itemController.addPost);
    server.use( router.routes() );
    server.listen(8081, () => {
        console.log('服务启动成功:http://localhost:8081')
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • controller/main.js
    const tpl = require('../libs/tpl');
    const itemsModel = require('../models/items');
    module.exports = {
        index: async ctx => {
            let items = await itemsModel.getItems();
            ctx.body = tpl.render('index.html', {
                items
            });  
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • controller/user.js
    const tpl = require('../libs/tpl');
    module.exports = {
        register: async ctx => {
            ctx.body = '注册';
        },
        login: async ctx => {
            ctx.body = '登陆'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • controller/item.js
    const tpl = require('../libs/tpl');
    const categoriesModel = require('../models/categories');
    const itemsModel = require('../models/items');
    module.exports = {
        add: async ctx => {
            let categories = await categoriesModel.getCategories();
            ctx.body = tpl.render('add-item.html', {
                categories
            });
        },
        addPost: async ctx => {
            let data = ctx.request.body;
            let files = ctx.request.files;
            let filename = '';
            if (files && files.cover) {
                let lastPos = files.cover.path.lastIndexOf('/');
                filename = files.cover.path.substring(lastPos+1);
            }
            let rs = await itemsModel.addItem([
                data.category_id,
                data.name,
                data.price,
                filename
            ]);
            console.log('rs', rs);
            ctx.body = '添加成功';   
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • libs/tpl.js
    const nunjucks = require('nunjucks');
    // 载入模板引擎
    const tpl = new nunjucks.Environment(
        // FileSystemLoader => node 模板文件加载
        new nunjucks.FileSystemLoader('views', {
            watch: true,
            noCache: true
        })
    );
    module.exports = tpl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • models/items.js
    const db = require('./model');
    module.exports = {
        getItems() {
            return new Promise( (resolve, reject) => {
                db.query("select * from `items`", function(err, rs) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(rs);
                    }
                });
            } )
        },
        addItem(newData) {
            return new Promise( (resolve, reject) => {
                db.query("insert into `items` (`category_id`, `name`, `price`, `cover`) values (?, ?, ?, ?)", newData, function(err, rs) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(rs);
                    }
                });
            } )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • models/categories.js
    const db = require('./model');
    module.exports = {
        getCategories() {
            return new Promise( (resolve, reject) => {
                db.query("select * from `categories`", function(err, rs) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(rs);
                    }
                });
            } )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • models/model.js
    const mysql2 = require('mysql2');
    // 数据链接不推荐使用use中间件
    let db = mysql2.createConnection({
        host: '127.0.0.1',
        port: 3306,
        user: 'root',
        password: 'Chen@123',
        database: 'test'
    });
    module.exports = db;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • views/index.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <link rel="stylesheet" href="/public/css/css.css" />
    head>
    <body>
        <div id="app">
            <header id="header">
                <a href="/" id="logo">a>
                <nav id="nav">
                    <a href="">1a>
                    <a href="">笔记本a>
                    <a href="">家居a>
                nav>
                <div id="user">
                    <a href="">登录a>
                    <a href="">注册a>
                div>
            header>
            <div id="main"> 
                <ul class="items-list">
                    {%for item in items%}
                    <li class="panel">
                        <img src="/public/upload/{{item.cover}}" alt="" class="cover">
                        <div class="name">{{item.name}}div>
                        <div class="price">¥ {{(item.price / 100).toFixed(2)}}div>
                    li>
                    {%endfor%}
                ul>
                <div class="pagination-container">
                    <div class="pagination">
                        <a href="" class="prev">上一页a>
                        <a href="">1a>
                        <a href="">2a>
                        <a href="">3a>
                        <a href="" class="current">4a>
                        <a href="">5a>
                        <a href="">6a>
                        <a href="">7a>
                        <a href="">8a>
                        <a href="" class="next">下一页a>
                    div>
        
                div>
            div>
        div>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • views/add-item.html
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <link rel="stylesheet" href="/public/css/css.css" />
    head>
    <body> 
        <header id="header">
            <a href="/" id="logo">a>
            <nav id="nav">
                <a href="">1a>
                <a href="">笔记本a>
                <a href="">家居a>
            nav>
            <div id="user">
                <a href="">登录a>
                <a href="">注册a>
            div>
        header>
        <div id="main">
            
           <div class="panel">
               <h2>添加新商品h2>
               <form action="" method="post" enctype="multipart/form-data">
                    <div class="form-item">
                        <label>
                            <span class="txt">商品分类:span>
                            <select name="category_id">
                                <option value="">请选择商品分类option>
                                {%for category in categories%}
                                <option value="{{category.id}}">{{category.name}}option>
                                {%endfor%}
                            select>
                        label>
                    div>
                    <div class="form-item">
                        <label>
                            <span class="txt">商品名称:span>
                            <input type="text" class="form-input" name="name">
                        label>
                    div>
                    <div class="form-item">
                        <label>
                            <span class="txt">价格:span>
                            <input type="text" class="form-input" name="price">
                        label>
                    div>
                    <div class="form-item">
                        <label>
                            <span class="txt">封面:span>
                            <input type="file" name="cover" />
                        label>
                    div>
                    <div class="form-item">
                        <label>
                            <span class="txt">span>
                            <button class="form-button primary">确认添加button>
                        label>
                    div>
               form>
           div>    
        div>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • public下的静态文件省略…
  • 相关阅读:
    C++中容易遗忘的知识点一
    StarRocks入门之路
    国产Linux音视频聊天程序开发遇到的坑及解决:相互听不到对方声音?
    自定义View3-水波纹扩散(仿支付宝咻一咻)实现代码、思想
    JavaScript基础
    天翼云铸牢国云安全,护航千行百业
    【随笔记】我的 CSDN 两周年创作纪念日
    项目进度管理有哪些方法?项目管理中的进度管理
    typescript
    spring boot实现短信验证码功能
  • 原文地址:https://blog.csdn.net/weixin_44178305/article/details/127954698