Midway.js探索与实践

简介: Midway Serverless 是用于构建 Node.js 云函数的 Serverless 框架。帮助您在云原生时代大幅降低维护成本,更专注于产品研发,而其专注于函数即服务,你只需要编写JavaScript函数就可以像编写Java接口一样的简单,并且提供了开箱即用的部署解决方案。

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

前言

我司的技术基建在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>
)

前端效果:

image.png

获取验证码:

image.png

校验:

image.png

部署接口

部署接口很方便,在Midway项目中执行npm run deploy即可进入部署流程,需前置准备阿里云 or 其他服务器账号,阿里云首次部署需要accountIdaccountKeyaccountSecret

具体文档在这里:

Midway接口部署方案

总结

如果你没有Serverless相关概念,通过本文了解Midway是一个快速入门认知到概念的方式,Midway的能力有很多,可以继续在官方文档中探索。

目录
相关文章
|
4月前
|
存储 JavaScript 前端开发
使用JavaScript构建动态交互式网页:从基础到实践
【10月更文挑战第12天】使用JavaScript构建动态交互式网页:从基础到实践
269 1
|
4月前
|
JavaScript 前端开发 安全
TypeScript的优势与实践:提升JavaScript开发效率
【10月更文挑战第8天】TypeScript的优势与实践:提升JavaScript开发效率
|
4月前
|
JavaScript 前端开发 开发者
理解JavaScript中的原型链:基础与实践
【10月更文挑战第8天】理解JavaScript中的原型链:基础与实践
|
30天前
|
缓存 NoSQL JavaScript
Vue.js应用结合Redis数据库:实践与优化
将Vue.js应用与Redis结合,可以实现高效的数据管理和快速响应的用户体验。通过合理的实践步骤和优化策略,可以充分发挥两者的优势,提高应用的性能和可靠性。希望本文能为您在实际开发中提供有价值的参考。
56 11
|
6月前
|
数据采集 Web App开发 JavaScript
利用Selenium和XPath抓取JavaScript动态加载内容的实践案例
利用Selenium和XPath抓取JavaScript动态加载内容的实践案例
|
2月前
|
存储 网络架构
Next.js 实战 (四):i18n 国际化的最优方案实践
这篇文章介绍了Next.js国际化方案,作者对比了网上常见的方案并提出了自己的需求:不破坏应用程序的目录结构和路由。文章推荐使用next-intl库来实现国际化,并提供了详细的安装步骤和代码示例。作者实现了国际化切换时不改变路由,并把当前语言的key存储到浏览器cookie中,使得刷新浏览器后语言不会失效。最后,文章总结了这种国际化方案的优势,并提供Github仓库链接供读者参考。
105 5
|
3月前
|
缓存 监控 JavaScript
Vue.js 框架下的性能优化策略与实践
Vue.js 框架下的性能优化策略与实践
|
3月前
|
JavaScript 前端开发 API
Vue.js 3:深入探索组合式API的实践与应用
Vue.js 3:深入探索组合式API的实践与应用
|
3月前
|
缓存 负载均衡 JavaScript
构建高效后端服务:Node.js与Express框架实践
在数字化时代的浪潮中,后端服务的重要性不言而喻。本文将通过深入浅出的方式介绍如何利用Node.js及其强大的Express框架来搭建一个高效的后端服务。我们将从零开始,逐步深入,不仅涉及基础的代码编写,更会探讨如何优化性能和处理高并发场景。无论你是后端新手还是希望提高现有技能的开发者,这篇文章都将为你提供宝贵的知识和启示。
|
4月前
|
前端开发 JavaScript
深入理解JavaScript中的事件循环(Event Loop):从原理到实践
【10月更文挑战第12天】 深入理解JavaScript中的事件循环(Event Loop):从原理到实践
59 1

热门文章

最新文章

  • 1
    当面试官再问我JS闭包时,我能答出来的都在这里了。
    49
  • 2
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 3
    Node.js 中实现多任务下载的并发控制策略
    34
  • 4
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    26
  • 5
    【JavaScript】深入理解 let、var 和 const
    49
  • 6
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    47
  • 7
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    57
  • 8
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    57
  • 9
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    72
  • 10
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    57