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')
});
  • 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
        });  
    }
}
  • controller/user.js
const tpl = require('../libs/tpl');
module.exports = {
    register: async ctx => {
        ctx.body = '注册';
    },
    login: async ctx => {
        ctx.body = '登陆'
    }
}
  • 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 = '添加成功';   
    }
}
  • 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;
  • 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);
                }
            });
        } )
    }
}
  • 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);
                }
            });
        } )
    }
}
  • 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;
  • 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>Document</title>
    <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="">1</a>
                <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="">1</a>
                    <a href="">2</a>
                    <a href="">3</a>
                    <a href="" class="current">4</a>
                    <a href="">5</a>
                    <a href="">6</a>
                    <a href="">7</a>
                    <a href="">8</a>
                    <a href="" class="next">下一页</a>
                </div>
    
            </div>
        </div>
    </div>
</body>
</html>
  • 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>Document</title>
    <link rel="stylesheet" href="/public/css/css.css" />
</head>
<body> 
    <header id="header">
        <a href="/" id="logo"></a>
        <nav id="nav">
            <a href="">1</a>
            <a href="">笔记本</a>
            <a href="">家居</a>
        </nav>
        <div id="user">
            <a href="">登录</a>
            <a href="">注册</a>
        </div>
    </header>
    <div id="main">
        <!-- 
            application/x-www-form-urlencoded: 把数据组织成 urlencode的格式
                key=value&key=value...
                适用于普通字符内容提交,提交的数据为纯字符
            multipart/form-data:二进制数据格式
                适用于提交的数据为非纯字符,如图片,视频等
         -->
       <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>
  • public下的静态文件省略…
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值