自从19年以来,基本开发都是用
vue
进行前端开发的,react
荒废了许久了,应该说自从react学趴了,就一直在那趴着的,再加上最近学习了koa+sequelize
,也想用这个做后端,写一下接口,于是就开始了这个项目
一、上效果图
1、前端展示
2、后端展示
二、后端使用koa+sequelize
为框架,mysql
作为后端数据库类型的
1、如何创建项目
1.1安装依赖
npm install -g koa-generator
1.2 在当前路径,使用koa-generator生成koa2项目(cmd命令行)
koa2 koa-test-sequelize-auto
1.3 进入koa-test-sequelize-auto目录(如果没有下载yarn的话,请全局下载yarn。 命令行: npm install -g yarn)
yarn install
生成的文档目录
├── bin
├── public
├── routes
├── view
├── package.json
└── app.js
1.4 启动项目
npm run dev
2、建立orm库
在当前项目中安装下面这几个依赖(cmd命令行)
yarn add sequelize
yarn add sequelize-auto
yarn add mysql
yarn add mysql2
3、开始项目搭建:采用MVC模式
在根目录下创建controllers,modules,schema,config文件夹
- controllers:写控制逻辑部分
- modules:写sql部分
- schema:写数据表部分
- config:写数据库配置部分
如图所示
4、使用 Sequelize 初始化连接池
在根目录创建 config/db.js 文件:(注意: 所连接的数据库必须存在,若不存在先创建数据库,创建数据库命令: CREATE DATABASE blogs DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;)
配置数据库
const Sequelize = require('sequelize')
const sequelize = new Sequelize('koa','root','123456',{
host:'localhost',
dialect:'mysql',
operatorsAliases:false,
dialectOptions:{
//字符集
charset:'utf8mb4',
collate:'utf8mb4_unicode_ci',
supportBigNumbers: true,
bigNumberStrings: true
},
pool:{
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
timezone: '+08:00' //东八时区
})
module.exports = {
sequelize
}
5、模型创建
在schema目录下创建tags.js
const Sequelize = require("sequelize");
const moment = require("moment");
const tag = (sequelize, DataTypes) => {
return sequelize.define(
"tag",
{
id: {
type: DataTypes.INTEGER(11),
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
unique: {
msg: "已添加",
},
field: "name"
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
get() {
return moment(this.getDataValue("createdAt")).format(
"YYYY-MM-DD HH:mm"
);
},
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
get() {
return moment(this.getDataValue("updatedAt")).format(
"YYYY-MM-DD HH:mm"
);
},
},
},
{
freezeTableName: true,
}
);
}
module.exports = tag;
5、模型使用
在modules目录下创建tags.js
/*
* @Author: Booble
* @Date: 2021-05-Th 03:45:47
* @Last Modified by: Booble
* @Last Modified time: 2021-05-Th 03:45:47
*/
const db = require("../config/db");
const Sequelize = db.sequelize;
const Tags = Sequelize.import("../schema/tags.js");
Tags.sync({ alter: true });
class TagsModule {
static async gettags(query, limit) { --分页列表
return await Tags.findAndCountAll({
where: {
...query,
},
order: [["id", "DESC"]],
offset: limit.offset,
limit: limit.limit,
});
}
static async gettagsAll() { --总条数
const list = await Tags.findAll(
{attributes: ['id', 'name']} --只显示该字段
);
return list
}
static async delTag(id) { --删除标签
return await Tags.destroy({
where: {
id,
},
});
}
static async updateTag(data) { --更新便签
const { id, name } = data;
return await Tags.update(
{
id,
name,
},
{
where: {
id,
},
}
);
}
static async addTag(query) { --添加标签
return await Tags.create({
name: query.name,
});
}
}
module.exports = TagsModule;
6、控制层写逻辑
在controllers目录下创建tags.js
/*
* @Author: Booble
* @Date: 2021-05-Th 04:36:42
* @Last Modified by: Booble
* @Last Modified time: 2021-05-Th 04:36:42
*/
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
const TagModule = require("../modules/tags");
class TagControler {
static async taglist(ctx) { //查询列表
let ctx_query = ctx.request.body;
try {
let pageSize = ctx_query.pageSize;
let page = ctx_query.pageNo ? (ctx_query.pageNo - 1) * pageSize : 0;
let sql = {
name: {
[Op.like]: "%" + ctx_query.name + "%",
},
};
let limitpage = {
offset: page,
limit: pageSize,
};
const { rows: result, count: total } = await TagModule.gettags(
sql,
limitpage
);
ctx.response.status = 200;
if (result) {
ctx.body = {
code: 200,
data: result,
total: total ? total: 0,
};
} else {
ctx.body = {
code: 400,
message: "请求连接错误",
};
}
} catch (err) {
ctx.response.status = 500;
ctx.body = {
code: 500,
message: "服务器错误,请求失败!",
};
}
}
static async getAll(ctx){ --获取总条数
try {
const data = await TagModule.gettagsAll();
ctx.response.status = 200;
if (data) {
ctx.body = {
code: 200,
data: data,
message: "获取成功",
};
} else {
ctx.body = {
code: 201,
message: "获取失败",
};
}
} catch (err) {
const msg = err.errors[0];
ctx.body = {
code: 500,
message: msg.value + msg.message,
};
}
}
static async addtag(ctx) {
let query = ctx.request.body;
if (!query.name) {
ctx.response.status = 200;
ctx.body = {
code: 1003,
desc: "标签不能为空",
};
return false;
}
try {
const data = await TagModule.addTag(query);
ctx.response.status = 200;
if (data) {
ctx.body = {
code: 200,
message: "新增成功",
};
} else {
ctx.body = {
code: 201,
message: "新增失败",
};
}
} catch (err) {
const msg = err.errors[0];
ctx.body = {
code: 500,
message: msg.value + msg.message,
};
}
}
static async deletag(ctx) { //删除
//const { id } = ctx.request.body;
const { id } = ctx.params;
try {
const result = await TagModule.delTag(id);
if (result) {
ctx.response.status = 200;
ctx.body = {
code: 200,
message: "数据删除成功",
};
}
} catch (err) {
ctx.response.status = 500;
ctx.body = {
code: 500,
message: "服务器错误,请求失败!",
};
}
}
static async updatetag(ctx) { //更新
let query = ctx.request.body;
try {
const result = await TagModule.updateTag(query);
if (result) {
ctx.response.status = 200;
ctx.body = {
code: 200,
message: "数据更改成功",
};
}
} catch (err) {
ctx.response.status = 500;
ctx.body = {
code: 500,
message: "服务器错误,请求失败",
};
}
}
}
module.exports = TagControler;
7、router接口配置
在routes目录下创建tags.js
const router = require("koa-router")();
const Tag = require("../controller/tags");
router.prefix("/api"); --路由前缀
router.post("/taglist", Tag.taglist);
router.get("/tag/alllist", Tag.getAll);
router.post("/tag/add", Tag.addtag);
router.del("/tag/delete/:id", Tag.deletag);
router.post("/tag/edit", Tag.updatetag);
module.exports = router;
如何部署到宝塔服务器
具体可以看一下这个 https://www.cnblogs.com/yingyigongzi/p/13926828.html
还有在宝塔面板安全里面,要把后端服务的端口放行,很重要,不然访问不了接口
三、前端采用的是react+antd
前端的目录为
├── public
├── index.html
└───src
│ │── api --接口
│ │—— assets --静态资源
│ │—— components --组件
| |—— config --配置
| |—— pages --页面
| |—— Router --路由
| |—— store --状态管理
| |—— utils --工具
└───index.js
└───serviceWorker.js --跨域配置
└───app.js
1、前端请求封装
1.1、在api文件夹下新建apiIP.js用来配置生产和开发的域名
//对外提供的服务地址
export const publicIp =
process.env.NODE_ENV === "development"
? "http://127.0.0.1:3001"
: "生产域名";
//对外提供获取图片的地址
export const logoImgIp = process.env.NODE_ENV === "development" ? "" : "";
// 登录路由
export const LOGIN = "";
1.2、在api下新建requiest.js
import axios from "axios";
import { publicIp } from "./apiIp";
import { message } from "antd";
import store from "../store";
let hide = null;
const instance = axios.create({
//创建axios实例,在这里可以设置请求的默认配置
timeout: 10000, // 设置超时时间10s
baseURL: publicIp, //根据自己配置的反向代理去设置不同环境的baeUrl
});
// 文档中的统一设置post请求头。下面会说到post请求的几种'Content-Type'
//instance.defaults.headers.post["Content-Type"] = "application/json";
instance.defaults.headers["Content-Type"] = "application/json;chartset=UTF-8";
let httpCode = {
//这里我简单列出一些常见的http状态码信息,可以自己去调整配置
400: "请求参数错误",
401: "权限不足, 请重新登录",
403: "服务器拒绝本次访问",
405: "Token已过期,请重新登录",
404: "请求资源未找到",
500: "内部服务器错误",
501: "服务器不支持该请求中使用的方法",
502: "网关错误",
504: "网关超时",
};
/** 添加请求拦截器 **/
instance.interceptors.request.use(
(config) => {
config.headers["token"] = store.getState().token || "";
hide = message.loading({ content: "Loading...", duration: 0 });
// 在这里:可以根据业务需求可以在发送请求之前做些什么:例如我这个是导出文件的接口,因为返回的是二进制流,所以需要设置请求响应类型为blob,就可以在此处设置。
if (config.url.includes("pur/contract/export")) {
config.headers["responseType"] = "blob";
}
// 我这里是文件上传,发送的是二进制流,所以需要设置请求头的'Content-Type'
if (config.url.includes("pur/contract/upload")) {
config.headers["Content-Type"] = "multipart/form-data";
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
/** 添加响应拦截器 **/
instance.interceptors.response.use(
(res) => {
hide()
if (res.status == "200") {
// 响应结果里的statusText: ok是我与后台的约定,大家可以根据实际情况去做对应的判断
return Promise.resolve(res.data);
}else{
this.props.history.push("/login");
}
// else {
// message.error(res.data.message);
// return Promise.reject(res.data.message);
// }
// return Promise.resolve(res);
},
(error) => {
hide()
if (error.response) {
// 根据请求失败的http状态码去给用户相应的提示
let tips =
error.response.status in httpCode
? httpCode[error.response.status]
: error.response.data.message;
message.error(tips);
if (error.response.status === 401) {
// token或者登陆失效情况下跳转到登录页面,根据实际情况,在这里可以根据不同的响应错误结果,做对应的事。这里我以401判断为例
//针对框架跳转到登陆页面
this.props.history.push("/admin/page");
}
return Promise.reject(error);
} else {
message.error("请求超时, 请刷新重试");
return Promise.reject("请求超时, 请刷新重试");
}
}
);
/* 统一封装get请求 */
export const get = (url, params, config = {}) => {
return new Promise((resolve, reject) => {
instance({
method: "get",
url,
params,
...config,
}).then((response) => {
resolve(response);
},err =>{
reject(err);
}).catch((error) => {
reject(error);
});
});
};
/* 统一封装post请求 */
export const post = (url, data, config = {}) => {
return new Promise((resolve, reject) => {
instance({
method: "post",
url,
data,
...config,
}).then((response) => {
resolve(response);
},err =>{
reject(err);
}).catch((error) => {
reject(error);
});
});
};
/* 统一封装del请求 */
export const del = (url, data, config = {}) => {
return new Promise((resolve, reject) => {
instance({
method: "delete",
url,
data,
...config,
}).then((response) => {
resolve(response);
},err =>{
reject(err);
}).catch((error) => {
reject(error);
});
});
};
/* 统一封装update请求 */
export const patch = (url, data, config = {}) => {
return new Promise((resolve, reject) => {
instance({
method: "patch",
url,
data,
...config,
}).then((response) => {
resolve(response);
},err =>{
reject(err);
}).catch((error) => {
reject(error);
});
});
};
1.3、请求方式和接口的挂载
在index.js里面设置
import React, { Component } from "react";
import ReactDOM from 'react-dom';
import './index.less';
import './assets/root.css';
import App from './App';
import { get, post, del, patch } from "./api/request";
import { apiUrl } from "./api/apiUrl";
import * as serviceWorker from './serviceWorker';
// import Router from './Router/router'
Component.prototype.get = get;
Component.prototype.post = post;
Component.prototype.del = del;
Component.prototype.patch = patch;
Component.prototype.api = apiUrl;
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
2、前端接口如何调用
如在tag.js的页面里
项目地址
前端博客展示 http://weblod.wxinxianyun.com/web/index
项目地址https://gitee.com/xinxianbaiguoshu/koa-react