Pipes
Les pipes transforment et valident les données de requête entrantes avant qu'elles n'atteignent vos méthodes de contrôleur. Ils sont exécutés après les middleware et avant vos contrôleurs de route, et opèrent sur des paramètres de route individuels.
Vue d'ensemble
Les pipes YasuiJS peuvent être utiles pour :
- Validation - Vérifier si les données entrantes répondent aux critères attendus
- Transformation - Convertir les données aux formats ou types souhaités
- Assainissement - Nettoyer et normaliser les données d'entrée
Les pipes peuvent être appliqués à trois niveaux :
- Niveau global - Appliqué à tous les paramètres de votre application
- Niveau contrôleur - Appliqué à tous les paramètres d'un contrôleur
- Niveau méthode - Appliqué aux paramètres dans des méthodes de route spécifiques
import { PipeTransform, IParamMetadata } from 'yasui';
@PipeTransform()
export class ValidationPipe implements IPipeTransform {
transform(value: any, metadata: IParamMetadata) {
// Logique de validation ici
return value;
}
}
Création de Pipes
Interface Pipe
Tous les pipes doivent implémenter l'interface IPipeTransform
avec une seule méthode transform
:
import { PipeTransform, IPipeTransform, IParamMetadata } from 'yasui';
@PipeTransform()
export class ParseIntPipe implements IPipeTransform {
transform(value: any, metadata: IParamMetadata): number {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) {
throw new HttpError(400, `Nombre attendu, reçu "${value}"`);
}
return parsed;
}
}
IParamMetadata
La méthode transform
reçoit les métadonnées sur le paramètre en cours de traitement :
type
: Source du paramètre (body, query, param, headers etc)name?
: Nom de la propriété dans l'objet source de la requêtemetatype?
: Type sous-jacent du paramètre, basé sur la définition du type dans le gestionnaire de route
Voir l'exemple dans la section Intégration avec Class Validator pour voir les métadonnées en utilisation.
Les pipes reçoivent des valeurs typées après la conversion automatique de type.
Utilisation des Pipes
Niveau Méthode
Appliquez les pipes aux méthodes de route spécifiques en utilisant @UsePipes()
:
@Controller('/users')
export class UserController {
@Get('/:id')
@UsePipes(ParseIntPipe)
getUser(@Param('id') id: number) {
// id est garanti d'être un nombre
return this.userService.findById(id);
}
}
Niveau Contrôleur
Appliquez les pipes à toutes les méthodes d'un contrôleur :
@Controller('/users')
@UsePipes(ValidationPipe, LoggingPipe)
export class UserController {
@Post('/')
createUser(@Body() createUserDto: CreateUserDto) {
// Tous les paramètres sont validés et journalisés pour toutes les routes
}
}
Niveau Global
Appliquez les pipes à tous les paramètres dans toute votre application :
yasui.createServer({
controllers: [UserController],
globalPipes: [ValidationPipe, TrimPipe]
});
Ordre d'Exécution
Les pipes s'exécutent dans cet ordre :
- Pipes globaux (dans l'ordre d'enregistrement)
- Pipes de contrôleur (dans l'ordre de déclaration)
- Pipes de méthode (dans l'ordre de déclaration)
// Configuration
yasui.createServer({
globalPipes: [GlobalPipe] // 1. Premier
});
@Controller('/users')
@UsePipes(ControllerPipe) // 2. Deuxième
export class UserController {
@Post('/')
@UsePipes(MethodPipe) // 3. Troisième
createUser(@Body() data: any) {
// les données ont été traitées par les trois pipes
}
}
Gestion des Erreurs
Les pipes peuvent lancer des erreurs pour rejeter les requêtes invalides, comme à tous les autres niveaux, celles-ci seront automatiquement capturées par Yasui :
@PipeTransform()
export class RequiredPipe implements IPipeTransform {
transform(value: any, metadata: IParamMetadata) {
if (value === undefined || value === null || value === '') {
const paramName = metadata.name || metadata.type;
throw new HttpError(HttpCode.BAD_REQUEST, `${paramName} est requis`);
}
return value;
}
}
Intégration avec Class Validator
Les pipes YasuiJS peuvent fonctionner parfaitement avec class-validator et class-transformer :
Voir l'exemple complet
import { validate, IsEmail, IsString, MinLength } from 'class-validator';
import { plainToInstance } from 'class-transformer';
import { PipeTransform, IPipeTransform, ParamMetadata, HttpError } from 'yasui';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(3)
name: string;
}
@Controller('/users')
export class UserController {
@Post('/')
@UsePipes(ValidationPipe) // Utilise les décorateurs class-validator
createUser(@Body() createUserDto: CreateUserDto) {
// createUserDto est validé et typé
return this.userService.create(createUserDto);
}
}
@PipeTransform()
export class ValidationPipe implements IPipeTransform {
async transform(value: any, metadata: ParamMetadata) {
if (metadata.type !== 'body' && metadata.type !== 'query') {
return value;
}
// Ignore la validation pour les types primitifs
if (!metadata.metatype || this.isPrimitiveType(metadata.metatype)) {
return value;
}
const object = plainToInstance(metadata.metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
const messages = errors.map(err =>
Object.values(err.constraints || {}).join(', ')
).join('; ');
throw new HttpError(400, `Échec de la validation : ${messages}`);
}
return object;
}
private isPrimitiveType(type: Function): boolean {
return [String, Boolean, Number, Array, Object].includes(type);
}
}