您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
前言
我司的技术基建在Midway
之上,主要是面向中后台前后端一体化方案,大白话就是全栈应用解决方案,什么是Midway
呢?
Midway Serverless
是用于构建 Node.js 云函数的 Serverless
框架。帮助您在云原生时代大幅降低维护成本,更专注于产品研发,而其专注于函数即服务,你只需要编写JavaScript
函数就可以像编写Java
接口一样的简单,并且提供了开箱即用的部署解决方案。
多编程范式
Midway
支持面向对象与函数式两种编程范式,你可以根据实际研发的需要,选择不同的编程范式来开发应用。
从官网中搬移两种案例,相同的hello midway
接口编写,是这样的:
面向对象(OOP + Class + IoC)
面向对象写法,采用类+装饰器的形式,可能看起来有点陌生~
// src/controller/home.ts
import { Controller, Get } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Controller('/')
export class HomeController {
@Inject()
ctx: Context
@Get('/')
async home() {
return {
message: 'Hello Midwayjs!',
query: this.ctx.ip
}
}
}
函数式(FP + Function + Hooks)
和React
很相像的一种写法~
import { useContext } from '@midwayjs/hooks'
import { Context } from '@midwayjs/koa';
export default async function home () {
const ctx = useContext<Context>()
return {
message: 'Hello Midwayjs!',
query: ctx.ip
}
}
本文将以OOP + Class + IoC
的形式来进行实践演示。
初始化构建项目
只需要两行命令,即可启动一个Midway
项目,你可以理解为一个启动一个后端服务。
npm init midway
npm run dev
在 controller
目录中,新建一个 src/controller/weather.controller.ts
文件,内容如下。
import { Controller, Get } from '@midwayjs/core';
@Controller('/')
export class WeatherController {
// 这里是装饰器,定义一个路由
@Get('/weather')
async getWeatherInfo(): Promise<string> {
// 这里是 http 的返回,可以直接返回字符串,数字,JSON,Buffer 等
return 'Hello Weather!';
}
}
这样你就可以通过前端请求的形式获取到/weather
接口了。
就像这样:
fetch('http://127.0.0.1/weather').then(res => {
res.json().then(data => {
console.log(data); // Hello Weather
})
})
对于@Controller
你可以理解为一个后端项目通过一个控制器来启动一个个接口,在里面包含了许多模块的服务,如user
类、list
类、upload
类等等,而user
中可能包含注册、登录、注销;upload
中可能包含上传、删除图片等等,所以你的接口看起来会像是这样的:
controller.ts
import { Controller, Post, Inject, Query, Get } from '@midwayjs/core';
import { UserService } from './service/user.service';
import { ListService } from './service/list.service';
@Controller('/')
export class CommonController {
@Inject()
ctx;
@Inject()
UserService: UserService;
@Inject()
ListService: ListService;
@Inject()
UploadService: UploadService;
@Post('/register')
async register(@Query('userId') userId: string, @Query('password') password: string): Promise<boolean> {
return this.UserService.register({userId, password});
}
@Get('/getUserInfo')
async getUserInfo() {
return this.UserService.getUserInfo();
}
// List和Upload的接口...
}
user.service.ts
import { Provide, Inject, Context } from '@midwayjs/core';
interface UserInfo {
userName: string;
age: number;
sex: string;
}
@Provide()
export class UserService {
@Inject()
ctx: Context;
async register(params): Promise<boolean> {
// 注册逻辑
return true;
}
async getUserInfo(): Promise<UserInfo> {
// 获取用户信息逻辑
return {
userName: '量子前端',
age: 20,
sex: '不详'
};
}
}
看起来有没有感受到编写一个接口就像是写一个函数/类一样简单呢?并且Midway
还提供了很多强大的功能,如中间件、组件、Http服务等等,接下来我们实践两个全栈场景,分别是图片上传和验证码,来具体感受一下。
案例
图片上传
首先安装依赖包。
npm i @midwayjs/upload@3 --save
在configuration.ts
中导入:
@Configuration({
imports: [upload],
importConfigs: [
{
default: defaultConfig,
prod: prodConfig,
},
],
conflictCheck: true,
})
接下来在控制器中声明并引用接口:
controller.ts
import {
Controller, Post, Inject, Files } from '@midwayjs/core';
import {
UploadService } from './service/upload.service';
@Controller('/')
export class CommonController {
@Inject()
UploadService: UploadService;
@Post('/upload')
async upload(@Files() files): Promise<string[]> {
return this.UploadService.upload(files);
}
}
upload.service.ts
官方有文件上传和流上传两种模式,这里以文件上传的方式将图片保存在Midway
项目的public
目录中。
import {
Provide, Inject, Context } from '@midwayjs/core';
import * as path from 'path';
import * as moment from 'moment';
import * as uuid from 'uuid';
import * as fs from 'fs';
@Provide()
export class UploadService {
@Inject()
ctx: Context;
async upload(files): Promise<string[]> {
const fileDir = path.join(this.ctx.app.getBaseDir(), '..', 'public');
const timeDir = `${moment().format('YYYY')}/${moment().format('MM-DD')}`;
const url = path.join(fileDir, timeDir);
const fileList = [];
if (!fs.existsSync(url)) fs.mkdirSync(url, {
recursive: true });
for (let i = 0; i < files.length; i++) {
const file = files[i];
const extname: string = path.extname(file.filename).toLowerCase();
const data = fs.readFileSync(file.data);
const fileName = uuid.v1();
const target = path.join(url, `${fileName}${extname}`);
fs.writeFileSync(target, data);
fileList.push(`${url}/${fileName}${
extname}`);
}
return fileList;
}
}
接下来我们简单写一个前端请求来测试。
const fileUpload = (e) => {
const formData = new FormData();
formData.append('file', e.target.files[0]);
console.log(e.target.files);
console.log(360, formData);
fetch('http://127.0.0.1:7002/upload', {
method: 'POST',
body: formData,
}).then((res) => {
res.json().then((data) => {
// 获取到图片上传的fileList,回显在DOM中
});
});
}
return (
<input type="file" onChange={
fileUpload} />
)
就这样一个简单基础版本的图片上传接口就写完啦~
验证码校验
首先安装依赖包。
npm i @midwayjs/captcha@3 --save
在configuration.ts
中导入:
@Configuration({
imports: [captcha],
importConfigs: [
{
default: defaultConfig,
prod: prodConfig,
},
],
conflictCheck: true,
})
然后我们声明两个接口,分别是获取验证码
接口和验证码校验
接口,这里以图形验证码为例:
controller.ts
import {
Controller, Post, Inject, Get } from '@midwayjs/core';
import {
CaptchaService } from '@midwayjs/captcha';
@Controller('/')
export class CommonController {
@Inject()
CaptchaService: CaptchaService;
@Get('/get-image-captcha')
async getImageCaptcha() {
const {
id, imageBase64 } = await this.CaptchaService.image({
width: 120,
height: 40,
size: 6,
type: 'number',
});
return {
id, // 验证码 id
imageBase64, // 验证码 SVG 图片的 base64 数据,可以直接放入前端的 img 标签内
};
}
// 验证验证码是否正确
@Post('/check-captcha')
async getCaptcha() {
const {
id, answer } = this.ctx.request.body;
const passed: boolean = await this.CaptchaService.check(id, answer);
return passed;
}
}
这里直接用官方的服务接口。
- 获取验证码接口直接返回给前端一个验证码图片id和图片base64地址;
- 校验验证码接口前端将验证的结果和获取验证码的id给后端来校验是否一致;
这里简单写一段react
伪代码调试一下:
const openCheckCaptchaModal = () => {
// 获取所有tab的商户数量,默认选中的tab不取,走列表
fetch('http://127.0.0.1:7002/get-image-captcha').then((res) => {
res.json().then(({
id, imageBase64 }) => {
Modal.alert({
content: (
<>
<img src={
imageBase64} />
<Form form={
form}>
<Form.Item name="captcha">
<Input placeholder="请输入验证码" />
</Form.Item>
</Form>
<span
onClick={
() => {
Modal.clear();
openCheckCaptchaModal();
}}
>
换一张
</span>
</>
),
onConfirm: () => {
const captcha = form.getFieldValue('captcha');
if (captcha) {
fetch('http://127.0.0.1:7002/check-captcha', {
method: 'POST',
body: JSON.stringify({
id,
answer: captcha,
}),
headers: {
'Content-Type': 'application/json',
},
}).then((res) => {
res.json().then((data) => {
if(data) {
return Message.success('验证成功');
}
});
});
}
},
});
});
});
};
return (
<span onClick={
openCheckCaptchaModal}>验证</span>
)
前端效果:
获取验证码:
校验:
部署接口
部署接口很方便,在Midway
项目中执行npm run deploy
即可进入部署流程,需前置准备阿里云 or 其他服务器账号,阿里云首次部署需要accountId
、accountKey
、accountSecret
。
具体文档在这里:
总结
如果你没有Serverless
相关概念,通过本文了解Midway
是一个快速入门认知到概念的方式,Midway
的能力有很多,可以继续在官方文档中探索。