likes
comments
collection
share

接口不是get就是post,put、delete确定不试一试吗?《闪调项目开发实践2》

作者站长头像
站长
· 阅读数 17

是否你的项目接口,不是 get 就是 post, 接下来让我们试一试 delete put等来编写接口

背景

最近在写闪调 闪调-前端调试项目, 写到了创建项目这一块, 之前写的接口,不是get就是post, 倦了倦了, 所以想尝试尝试restful,换一种范式和风格.

Restful Api

基本理解

关于RESTful的基本概念,《方法论:如何学习HTTP》也介绍过了,在这就不再赘述了。restful风格接口。

简单的说就是 接口名全是一样的,只是用post,put,delete区分 例如

  • post /api/project/xxx 创建项目
  • get /api/project/xxx 获取项目xxx
  • PUT /api/project/xxx 更新项目xxx
  • delete /api/project/xxx 删除项目xxx

特点: 使用相同路径不同的http方法表达不同的行为。

优点缺点各位评论区说说?

开发实践

接下来我们以项目的增删改查为例子, 以下代码使用 nodejs modwayjs框架和mongodb为例子。

代码github链接 github.com/girl-email/…

创建mongo的Project实体

mongo不像mysql要建表结构, 这边直接塞就好了。

// src/entity/project.ts
import { prop } from '@typegoose/typegoose';

/**
 * 存储Project信息实体
 */
export class Project {
    /**
     * 项目名称
     */
    @prop()
    public projectName?: string;

    /**
     * 项目描述
     */
    @prop()
    public projectDesc?: string;

    /**
     * appKey
     */
    @prop({ type: () => String})
    public appKey?: string;

    /**
     * userId
     */
    @prop({ type: () => String})
    public userId: string;

    /**
     * 创建时间
     */
    @prop({ type: () => Date, default: () => new Date()})
    public createTime?: Date;
}

创建service

service层描述

在业务中,只有控制器(Controller)的代码是不够的,一般来说会有一些业务逻辑被抽象到一个特定的逻辑单元中,我们一般称为服务(Service)。

接口不是get就是post,put、delete确定不试一试吗?《闪调项目开发实践2》

提供这个抽象有以下几个好处:

  • 保持 Controller 中的逻辑更加简洁。
  • 保持业务逻辑的独立性,抽象出来的 Service 可以被多个 Controller 重复调用。
  • 将逻辑和展现分离,更容易编写测试用例。

project的service层

import {InjectEntityModel} from '@midwayjs/typegoose';
import {ReturnModelType} from '@typegoose/typegoose';
import {Project} from '../entity/project';
import {Provide} from '@midwayjs/decorator';
const crypto = require("crypto");

/**
 * 项目
 */
@Provide()
export class ProjectService {

    @InjectEntityModel(Project)
    ProjectModel: ReturnModelType<typeof Project>;

    /**
     * 项目列表
     */
    getList(where?: any): Promise<Project> {
        return this.ProjectModel.find().where(where).exec();
    }

    /**
     * 创建appKey
     */
    createAppKey(id = ""): Promise<string> {
        const shasum = crypto.createHash('sha1');//返回sha1哈希算法
        shasum.update(this.appSecret + id);//指定要摘要的原始内容,可以在摘要被输出之前使用多次update方法来添加摘要内容
        return shasum.digest('hex');
    }

    /**
     * 创建项目
     */
    async createProject(param: Project) {
        const {_id: id} = await this.ProjectModel.create({
            projectName: param.projectName,
            projectDesc: param.projectDesc,
            userId: param.userId
        });  
        return id
    }
    ...
}

新增项目控制器

控制器 承载了路由的能力,每个控制器可以提供多个路由,不同的路由可以执行不同的操作。

接口不是get就是post,put、delete确定不试一试吗?《闪调项目开发实践2》 如下

// src/controller/project.controller.ts
import {Inject, Controller, Get, Post, Del, Body, Param} from '@midwayjs/decorator';
import {Context} from '@midwayjs/koa';
import {UserService} from '../service/user.service';
import {ProjectService} from '../service/project.service';
import {AliYunService} from '../service/aliyun.service';
import BaseController from "../core/baseController";
import {Put} from "@midwayjs/core";
import { AddProjectDTO } from './../dto/project';

/**
 * 闪调项目管理控制器
 */
@Controller('/api/project')
export class ProjectController extends BaseController {
    @Inject()
    ctx: Context;

    @Inject()
    userService: UserService;

    @Inject()
    projectService: ProjectService;
}

依赖注入

@Inject()
userService: UserService;

如上代码什么意思?

把userService实例一个放到userService 就是说赋值为对应的实例化对象

这个就叫依赖注入, 是控制反转的一种方式。

写过java、nestjs的同学应该很熟悉。

参数校验

使用midwayjs自带的组件 文档地址 www.midwayjs.org/docs/extens…

1、定义检查规则

按照上面的逻辑,我们需要 重新定义一个新的 Class,因为装饰器只能装饰在实际的 Class 上,而不是 interface。

为了方便后续处理,我们将 project 放到一个 src/dto 目录中。

Data Transfer Object(数据传输对象)DTO 是一组需要跨进程或网络边界传输的聚合数据的简单容器。它不应该包含业务逻辑,并将其行为限制为诸如内部一致性检查和基本验证之类的活动。

import { Rule, RuleType } from '@midwayjs/validate';

export class AddProjectDTO {
    @Rule(RuleType.string().required())
    projectName: string;

    @Rule(RuleType.string().required())
    projectDesc: string;
}

这个 AddProjectDTO class 提供了2个属性和他们对应的校验规则。

  • projectName 一个必填的字符串类型
  • projectDesc 一个必填的字符串类型

@Rule 装饰器用于 修饰需要被校验的属性,它的参数为 RuleType 对象提供的校验规则的链式方法。

2、校验参数

定义完类型之后,就可以直接在业务代码中使用了,开启校验能力还需要 @Validate 装饰器。 在控制器中使用

// src/controller/project.ts
import { Controller, Get, Provide } from '@midwayjs/core';
import { AddProjectDTO } from './dto/project';

/**
 * 增加项目
 */
@Post('/')
async addProject(@Body() param: AddProjectDTO) {
    const userId = this.ctx.user._id
    const result = await this.projectService.createProject({...param, userId});
    return this.success(result)
}

AddProjectDTO 的规则会去校验 body里的参数

一旦校验失败,会抛出如下错误

ValidationError: "projectName" is required

接口

restful接口 接口在上一步的控制器里写。

获取项目 get

// src/controller/project.controller.ts
/**
 * 获取单个
 */
@Get('/:id')
async getList(@Param('id') id) {
    const userId = this.ctx.user._id
    const result = await this.projectService.findOne({id, userId});
    return this.success(result)
}       

增加项目 post

// src/controller/project.controller.ts
/**
 * 增加项目
 */
@Post('/')
async addProject(@Body() param: AddProjectDTO) {
    const userId = this.ctx.user._id
    const result = await this.projectService.createProject({...param, userId});
    return this.success(result)
}    

编辑项目 Put

// src/controller/project.controller.ts
/**
 * 编辑项目
 */
@Put('/:id')
async editProject(@Param('id') id, @Body() body: AddProjectDTO) {
    const userId = this.ctx.user._id
    const result = await this.projectService.updateProject({...body, userId, id});
    return this.success(result)
}       

删除项目 delete

// src/controller/project.controller.ts
/**
 * 删除
 */
@Del('/:id')
async userInfo(@Param('id') id) {
    const result = await this.projectService.deleteProject(id);
    if (!result.deletedCount) {
        return this.fail(0, '该项目不存在')
    }
    return this.success(result, '操作成功')
}      

最后

尝试了一种新的风格吧。写下来感觉和post接口差不多,路由和接受参数变了,比如编辑用put方法, 变成了path参数和body参数, id在path, 其余参数在body。

发现有个不好打开控制台network里接口路径都一样。

你们怎么看restful呢???