接口不是get就是post,put、delete确定不试一试吗?《闪调项目开发实践2》
是否你的项目接口,不是 get
就是 post
, 接下来让我们试一试 delete
put
等来编写接口
背景
最近在写闪调 闪调-前端调试项目
, 写到了创建项目这一块, 之前写的接口,不是get就是post, 倦了倦了, 所以想尝试尝试restful,换一种范式和风格.
Restful Api
基本理解
关于RESTful的基本概念,《方法论:如何学习HTTP》也介绍过了,在这就不再赘述了。restful风格接口。
简单的说就是 接口名全是一样的,只是用post
,put
,delete
区分
例如
post
/api/project/xxx 创建项目get
/api/project/xxx 获取项目xxxPUT
/api/project/xxx 更新项目xxxdelete
/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)。
提供这个抽象有以下几个好处:
- 保持 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
}
...
}
新增项目控制器
控制器 承载了路由的能力,每个控制器可以提供多个路由,不同的路由可以执行不同的操作。
如下
// 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呢???
转载自:https://juejin.cn/post/7198434484235468837