# How to Add Custom Validation Rules for Admin Inputs in the Mall Project

> Learn to add custom validation rules for admin inputs in the Mall project. Implement ConstraintValidator and use @Validated annotation. Ensure data integrity before business logic.

- Repository: [macro/mall](https://github.com/macrozheng/mall)
- Tags: how-to-guide
- Published: 2026-02-28

---

**To add custom validation rules for admin inputs in the Mall project, define a JSR-303 annotation in the validator package, implement the `ConstraintValidator` interface with your validation logic, and apply the annotation to DTO fields; Spring Boot's `@Validated` annotation on controller methods automatically enforces these constraints before business logic executes.**

The macrozheng/mall repository secures its admin APIs using JSR-303 Bean Validation. When you need to enforce domain-specific constraints—such as validating that a product code matches a specific pattern or that a status flag contains only allowed values—you can extend this system without modifying existing controller code. This guide demonstrates the exact three-step pattern used in `mall-admin` to create and apply custom validation rules for admin input parameters.

## The Validation Architecture in Mall

The project combines **JSR-303 annotations** with Spring’s **`@Validated`** annotation to enforce input constraints. In [`mall-admin/src/main/java/com/macro/mall/controller/PmsBrandController.java`](https://github.com/macrozheng/mall/blob/main/mall-admin/src/main/java/com/macro/mall/controller/PmsBrandController.java), the `create` method demonstrates this pattern by accepting a `@Validated @RequestBody PmsBrandParam` parameter. When Spring processes the request, it automatically triggers all validators associated with annotations on that DTO’s fields.

The system relies on two core components located in `mall-admin/src/main/java/com/macro/mall/validator/`:

- **[`FlagValidator.java`](https://github.com/macrozheng/mall/blob/main/FlagValidator.java)** – A custom annotation that defines validation metadata (allowed values, error messages) and references its validator class via `@Constraint(validatedBy = FlagValidatorClass.class)`.
- **[`FlagValidatorClass.java`](https://github.com/macrozheng/mall/blob/main/FlagValidatorClass.java)** – The implementation that contains the actual validation logic, checking whether an integer field matches predefined allowed values.

When validation fails, the global exception handler `MallExceptionHandler` in `mall-common` intercepts the error and returns a **400 Bad Request** response containing the message defined in the annotation.

## Creating a Custom Validation Rule

To implement a new validation rule—such as enforcing that a product code is exactly 8 alphanumeric characters—you follow a three-step pattern identical to the built-in `FlagValidator` implementation.

### Step 1: Define the Annotation

Create a new annotation file in `mall-admin/src/main/java/com/macro/mall/validator/`. This annotation must be meta-annotated with `@Constraint` to link it to your validator implementation.

```java
// mall-admin/src/main/java/com/macro/mall/validator/ProductCode.java
package com.macro.mall.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(validatedBy = ProductCodeValidator.class)
public @interface ProductCode {
    String message() default "invalid product code";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

```

### Step 2: Implement the Validator

Implement `ConstraintValidator` in a companion class. The `initialize` method captures annotation parameters (if any), while `isValid` contains the logic that returns `true` for valid values or `false` for violations.

```java
// mall-admin/src/main/java/com/macro/mall/validator/ProductCodeValidator.java
package com.macro.mall.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;

public class ProductCodeValidator implements ConstraintValidator<ProductCode, String> {
    private static final Pattern PATTERN = Pattern.compile("^[A-Za-z0-9]{8}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && PATTERN.matcher(value).matches();
    }
}

```

### Step 3: Apply to the DTO

Add the annotation to fields in your request DTO located in `mall-admin/src/main/java/com/macro/mall/dto/`. The following example shows integration with `PmsProductParam` (or any admin parameter class):

```java
// mall-admin/src/main/java/com/macro/mall/dto/PmsProductParam.java
package com.macro.mall.dto;

import com.macro.mall.validator.ProductCode;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
public class PmsProductParam {
    @ProductCode(message = "商品编码必须是8位字母或数字")
    @ApiModelProperty(value = "商品唯一编码")
    private String productCode;

    // other fields...
}

```

## Reference Implementation: The FlagValidator Pattern

The existing `FlagValidator` in the codebase demonstrates the standard for boolean-like status fields. In [`FlagValidator.java`](https://github.com/macrozheng/mall/blob/main/FlagValidator.java), the annotation declares a `String[] value()` array that lists allowed integer values (e.g., `{"0","1"}`) and a default error message.

The corresponding `FlagValidatorClass` implements `ConstraintValidator<FlagValidator, Integer>`. Its `initialize` method stores the allowed values from the annotation, and its `isValid` method returns `true` when the supplied integer is either `null` (treated as optional) or matches any entry in the allowed list.

You can see this applied in [`PmsBrandParam.java`](https://github.com/macrozheng/mall/blob/main/PmsBrandParam.java), where `factoryStatus` and `showStatus` fields are annotated with:

```java
@FlagValidator(value = {"0","1"}, message = "厂家状态不正确")
private Integer factoryStatus;

```

When an admin submits an invalid value like `2`, Spring rejects the request with a 400 status and the message "厂家状态不正确" before the controller method body executes.

## How Spring Enforces Validation

Spring Boot’s `MethodValidationPostProcessor` (enabled implicitly by the `@Validated` annotation on controller methods) discovers any annotation meta-marked with `@Constraint`. It invokes the associated validator’s `isValid` method before the controller logic runs.

If any constraint fails, Spring collects the errors into a `BindingResult` and throws a `MethodArgumentNotValidException`. The `MallExceptionHandler` in `mall-common/src/main/java/com/macro/mall/common/exception/` catches this exception and formats it into a standardized JSON error response. **No additional configuration is required**—the validator auto-registers because it implements `ConstraintValidator` and the annotation carries `@Constraint`.

## Summary

- **Define the rule**: Create an annotation in `mall-admin/src/main/java/com/macro/mall/validator/` and annotate it with `@Constraint(validatedBy = YourValidator.class)`.
- **Implement the logic**: Create a class implementing `ConstraintValidator<YourAnnotation, FieldType>` with custom validation logic in the `isValid` method.
- **Apply to inputs**: Add the annotation to fields in DTOs located in `mall-admin/src/main/java/com/macro/mall/dto/`.
- **Automatic enforcement**: Ensure controller methods use `@Validated` on request parameters; Spring automatically validates inputs and returns 400 Bad Request for violations via `MallExceptionHandler`.

## Frequently Asked Questions

### Where should I place custom validator files in the Mall project?

Place the annotation interface and validator implementation in `mall-admin/src/main/java/com/macro/mall/validator/`, following the pattern established by [`FlagValidator.java`](https://github.com/macrozheng/mall/blob/main/FlagValidator.java) and [`FlagValidatorClass.java`](https://github.com/macrozheng/mall/blob/main/FlagValidatorClass.java). This keeps all validation logic centralized and discoverable by Spring's component scan.

### How does the Mall project format validation error responses?

Validation failures trigger the `MallExceptionHandler` class located in `mall-common`. This global exception handler catches `MethodArgumentNotValidException` and returns a **400 Bad Request** response containing the specific error message defined in your annotation’s `message()` attribute (e.g., "商品编码必须是8位字母或数字").

### Can I validate individual method parameters instead of entire DTOs?

Yes. You can apply your custom annotation directly to method parameters in the controller and add `@Validated` at the class level or method level. For example, `@PostMapping("/test") public Result test(@Validated @ProductCode String code)` validates the parameter directly without requiring a wrapper DTO.

### Do I need to register the validator in a Spring configuration class?

No. Spring Boot auto-discovers validators that implement `ConstraintValidator` because the annotation is meta-annotated with `@Constraint`. As long as your validator class resides in a scanned package (such as `com.macro.mall.validator`), it automatically wires into the validation pipeline without explicit bean registration.