请求校验
校验数据类型
运行时校验TypeScript类型
Summer支持运行时校验TypeScript类型,所以请求DTO只需要按正常的TypeScript写法就可以了,必须写成class类,不能是interface
支持的验证类型
类型 | 说明 |
---|---|
boolean | 布尔型 |
number | 数字型 |
string | 字符串型 |
int | 整形 |
bigint | 大整型 |
Date | 日期类型 |
enum | 枚举(支持数字枚举和字符串枚举) |
't1' | 't2' | 字符串联合类型 |
{object} | JSON对象 |
array[] | 数组 |
generic<T> | 简单的泛型 |
数据限制装饰器
装饰器 | 用途 |
---|---|
? | 选传参数 |
! | 使用在 string 不允许为空白(多个空格) |
@Min(min: number, message?: string | (val) => string) | 使用在 number/int/bigint 限定最小值 |
@Max(max: number, message?: string | (val) => string) | 使用在 number/int/bigint 限定最大值 |
@MinLen(minLen: number, message?: string | (val) => string) | 使用在 string/array[] 限定最小长度 |
@MaxLen(maxLen: number, message?: string | (val) => string) | 使用在 string/array[] 限定最大长度 |
@Len(fixedLen: number, message?: string | (val) => string) | 使用在 string/array[] 限定固定长度 |
@Len(minLen: number, maxLen: number, message?: string | (val) => string) | 使用在 string/array[] 限定长度范围 |
@Email(message?: string | (val) => string) | 使用在 string 限定必须是电子邮件格式 |
@Pattern(regexp :RegExp, message?: string | (val) => string) | 使用在 string 匹配正则 |
@Validate(func: (val: any) => boolean | string ) | 自定义验证,验证函数返回 true 时代表验证通过, 返回字符串 "错误信息" 表示验证失败。 |
请求验证例子
src/dto/request/book.ts
enum BookType {
Fiction = 0,
Education = 1
}
class AddBookRequest {
// 验证 title 必须是字符串,而且是个选传参数
title?: string
// 验证 author 必须是字符串
author: string
// 验证 type 必须是 "Fiction" 或 "Education"
type: BookType
// 验证 pageCount 必须是整形
pageCount: int
}
src/controller/BookController.ts
import { Controller, Post, Body } from '@summer-js/summer'
import { AddBookRequest } from '../dto/request/book'
@Controller('/v1')
export class BookController {
@Post('/books')
add(@Body addBookRequest: AddBookRequest) {
console.log(addBookRequest)
}
}
类型验证
整形验证
int 不是TS/JS的基本类型,这是 Summer 扩展用于做请求验证的类型,在常规代码中int等同于 number.
src/controller/BookController.ts
import { Controller, Get, PathParam } from '@summer-js/summer'
@Controller
export class BookController {
@Get('/books/:inx')
book(@PathParam inx: int) {
const books = ['Harry Potter', 'The Great Gatsby', 'Dune']
return books[inx]
}
}
Get http://127.0.0.1:8801/todos/1.5
{
message: "Validation Failed",
errors: [
{
param: "inx",
message: "'1.5' is not an integer"
}
]
}
枚举验证
枚举类型验证传入值必须是枚举的其中一个key值
src/controller/BookController.ts
import { Controller, Get, Query } from '@summer-js/summer'
enum BookType {
Fiction = 0,
Education = 1
}
@Controller
export class BookController {
@Get('/books')
addBook(@Query type: BookType) {
if (type === BookType.Education) {
console.log('Searching Education Books...')
} else if (type === BookType.Fiction) {
console.log('Searching Fiction Books...')
}
}
}
GET http://127.0.0.1:8801/books?type=Romance
{
"message":"Validation Failed",
"errors":[
{
"param":"type",
"message":"'Romance' is not in [\"Fiction\",\"Education\"]"
}
]
}
GET http://127.0.0.1:8801/books?type=Fiction
Searching Fiction Books...
日期验证
日期验证传入的格式是否为日期格式例如 '2022-10-01' 或 '2022-10-01 12:00:00',亦可以使用时间戳 '1355270400000' 或 1355270400000
src/controller/BookController.ts
import { Controller, Get, Query } from '@summer-js/summer'
@Controller
export class BookController {
@Get('/books')
addBook(@Query createDate: Date) {
console.log('Searching Books in created in ' + createDate)
}
}
GET http://127.0.0.1:8801/books?createDate=xxx
{
"message":"Validation Failed",
"errors":[
{
"param":"createDate",
"message":"error parsing 'xxx' to Date"
}
]
}
布尔值验证
字符串 'true'/'false' '0'/'1', 数字 0/1 都可以用于验证通过并转成布尔型
Validation boolean query data
import { Controller, Get, Query } from '@summer-js/summer';
@Controller
export class ExampleController {
@Get('/boolean-test')
hello(@Query isDone: boolean) {
console.log(typeof isDone, isDone);
}
}
GET http://localhost:8801/boolean-test?isDone=true
boolean true
GET http://localhost:8801/boolean-test?isDone=0
boolean false
数字型数组验证
带逗号的字符串 "1,3,10" 可以通过int[]/number[]的验证
src/controller/BookController.ts
import { Controller, Delete, PathParam } from '@summer-js/summer'
@Controller
export class BookController {
@Delete('/books/:ids')
deleteBooks(@PathParam ids: int[]) {
console.log('Delete Books id in ' + JSON.stringify(ids))
}
}
http://127.0.0.1:8801/books/12,15,31
Delete Books id in [12,15,31]
数据约束
import { Controller, Post, Body, Min, MaxLen } from '@summer-js/summer';
enum BookType {
Fiction = 0,
Education = 1
}
class AddBookRequest {
title: string;
// no more than 50 chars
@MaxLen(50)
author: string;
type: BookType;
// pageCount must bigger than 0
@Min(1)
pageCount: int;
}
@Controller('/v1')
export class BookController {
@Post('/books')
add(@Body addBookRequest: AddBookRequest) {
console.log(addBookRequest);
}
}
必传与选传
必传与选传
使用 '?' 标记的字段为选传参数,选传参数可以给默认值。
Summer的选传必须给API接口做了最佳适配,必传代表了必须要有数据,所以必传还有非空的概念。不写 '?' 的传参在传空字符串的时候不会验证通过。
class PersonRequest{
name: string
age?: number = 12
}
notice the '?' optional token also works in method params
@Get(/books)
addBooks(@Query keyword?: string, @Query pageNumber = 1){
// code
}
部分更新
src/dto/request/book.ts
export class Book {
title: string;
author: Person;
}
src/controller/BookController.ts
import { Controller, Patch, Body } from '@summer-js/summer';
import { Book } from '../dto/request/book'
@Controller
export class BookController {
@Patch('/books/:id')
updateBook(@Body book: Partial<Book>) {
console.log(book);
}
}
对象体验证
src/dto/request/book.ts
export enum Gender {
Female = 1,
Male = 2
}
export class Person {
name: string;
age: int;
gender: Gender;
}
export class Book {
title: string;
author: Person;
}
import { Controller, Post, Body } from '@summer-js/summer';
import { Book } from '../dto/request/book'
@Controller
export class BookController {
@Post('/books')
addBook(@Body book: Book) {
console.log(typeof book, book);
}
}
对象体数组验证
src/dto/request/book.ts
export class Book {
title: string;
author: string;
}
src/controller/BookController.ts
import { Controller, Post, Body } from '@summer-js/summer';
import { Book } from '../dto/request/book'
@Controller
export class BookController {
@Post('/books')
addBooks(@Body param: Book[]) {
console.log(typeof param, param);
}
}
类继承验证
src/dto/request/animal.ts
class Animal {
name: string
weight: number
}
export class Dog extends Animal {
noseLength: number
eyesColor: 'blue' | 'brown'
}
src/controller/AnimalController.ts
import { Controller, Post, Body } from '@summer-js/summer'
import { Dog } from '../dto/request/animal'
@Controller
export class AnimalController {
@Post('/dogs')
add(@Body dog: Dog) {
console.log(typeof dog, dog)
}
}
自定义验证
src/dto/request/book.ts
import { Validate } from '@summer-js/summer'
export class Book {
@Validate((val: string) => {
if(val.substring(0, 1).toUpperCase() === val.substring(0, 1)){
// return true to pass validation
return true
}
// or return an error message
return "Title must starts with uppercase letter"
})
title: string
}
src/controller/BookController.ts
import { Controller, Body, Post } from '@summer-js/summer'
import { Book } from '../dto/request/book'
@Controller
export class BookController {
@Post('/books')
detail(@Body book: Book) {
return book
}
}
简单的泛型验证
src/dto/request/animal.ts
export class Dog {
name: string
weight: number
}
export class Cat {
name: string
tailLength: number
}
export class AnimalRequest<T>{
obj: T
count: number
}
src/controller/AnimalController.ts
import { Controller, Post, Body } from '@summer-js/summer'
import { Dog, Cat, AnimalRequest } from '../dto/request/animal'
@Controller
export class AnimalController {
@Post('/dogs')
addDog(@Body dog: AnimalRequest<Dog>) {
console.log(dog)
}
@Post('/cats')
addCat(@Body cat: AnimalRequest<Cat>) {
console.log(cat)
}
}
Summer中的泛型验证
Summer 只支持简单的泛型.
复杂的泛型无法验证(包扩多层嵌套,复杂结构)
class TObj<T>{
a: T
}
class PObj{
a: number
}
class Obj<T, K, G> {
field1: T
field2: K
field3: G
filed4: TObj<int>
}
// 可以验证
@Post
api(@Body body: Obj<int[], string, boolean>) { }
// 可以验证
@Post
api(@Body body: Obj<int[], string, PObj>) { }
// 不可使用,可能会导致程序错误
@Post
api(@Body body: Obj<int[], string, TObj<TObj<TObj<number>>>>) { }