AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79412128
Accepted
PatPanda
PatPanda
Asked: 2025-02-04 22:58:05 +0800 CST2025-02-04 22:58:05 +0800 CST 2025-02-04 22:58:05 +0800 CST

SpringBoot @Valid em um campo, com base no valor de outro campo

  • 772

Gostaria de usar o SpringBoot @Valid para validar um campo de solicitação http, mas com base em outro campo da mesma solicitação http.

Tenho o seguinte código:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <artifactId>question</artifactId>

    <properties>
        <maven.compiler.source>23</maven.compiler.source>
        <maven.compiler.target>23</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
class FieldValidationApplication {

    public static void main(String[] args) {
        SpringApplication.run(FieldValidationApplication.class, args);
    }

}
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
class FieldValidationController {

    @PostMapping("/validate")
    String question(@Valid @RequestBody SomeRequest someRequest) {
        return "please validate the field";
    }

}

record SomeRequest(int score,
                   String fieldPositive,
                   String fieldZeroAndNegative
                   ) 
{ }

As regras de validação são bastante simples:

O payload da requisição tem um field score. Se o valor do field score for estritamente positivo, então preciso verificar se o campo fieldPositive é uma string válida e também se fieldZeroAndNegative é nulo.

Por exemplo:

{
  "score": 1,
  "fieldPositive": "thisisok"
}

Mas estes não são:

{
  "score": 1
}

{
  "score": 1,
  "fieldPositive": ""
}

{
  "score": 1,
  "fieldPositive": "below fieldZeroAndNegative should be null",
  "fieldZeroAndNegative": "not ok"
}

Regra semelhante para o outro campo (código logo abaixo).

Foi isso que tentei, criei uma anotação personalizada:

record SomeRequest(int score,
                   @ValidateThisFieldOnlyIfScoreIsPositive String fieldPositive,
                   @ValidateThisFieldOnlyIfScoreIsZeroOrNegative String fieldZeroAndNegative
                   ) 
{ }

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = ValidateThisFieldOnlyIfScoreIsPositiveValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidateThisFieldOnlyIfScoreIsPositive
{
    String message() default "Field is invalid";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}


import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

class ValidateThisFieldOnlyIfScoreIsPositiveValidator implements ConstraintValidator<ValidateThisFieldOnlyIfScoreIsPositive, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        System.out.println("hello, the value of the field fieldPositive is " + value);
        System.out.println("However, I cannot get the value of the field score");
        if (" SomeRequest score " > 0) { //how to get the value of the field score here?
            return value != null && !value.isEmpty() && value.length() > 3;
        }
        if (" SomeRequest score"  <= 0) {
            return value == null;
        }
        ...
    }

}

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = ValidateThisFieldOnlyIfScoreIsZeroOrNegativeValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface ValidateThisFieldOnlyIfScoreIsZeroOrNegative
{
    String message() default "Field is invalid";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

class ValidateThisFieldOnlyIfScoreIsZeroOrNegativeValidator implements ConstraintValidator<ValidateThisFieldOnlyIfScoreIsZeroOrNegative, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        System.out.println("hello, the value of the field fieldZeroAndNegative is " + value);
        System.out.println("However, I cannot get the value of the field score");
        if (" SomeRequest score " <= 0) { //how to get the value of the field score here?
            return value != null && !value.isEmpty() && value.length() > 3;
        }
        if (" SomeRequest score" > 0) {
            return value == null;
        }

    }

}

Não tenho certeza se usar uma anotação por campo é a melhor opção para começar.

Pergunta:

Como obter ambos os campos (ou vários campos) da mesma solicitação no validador?

java
  • 1 1 respostas
  • 49 Views

1 respostas

  • Voted
  1. Best Answer
    Jerre
    2025-02-04T23:18:32+08:002025-02-04T23:18:32+08:00

    Para validar um campo com base no valor de outro campo no mesmo objeto de solicitação, você precisa aplicar a validação no nível de classe em vez do nível de campo. Dessa forma, o validador tem acesso a todos os campos do objeto.

    Solução: Use uma restrição de nível de classe

    1. Crie uma anotação de validação personalizada

    Crie uma anotação personalizada que pode ser aplicada a toda a classe:

    import jakarta.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Constraint(validatedBy = SomeRequestValidator.class)
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ValidSomeRequest {
        String message() default "Invalid request data";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    1. Implementar o Validador

    Crie um validador que possa acessar todos os campos de SomeRequest:

    import jakarta.validation.ConstraintValidator;
    import jakarta.validation.ConstraintValidatorContext;
    
    public class SomeRequestValidator implements ConstraintValidator<ValidSomeRequest, SomeRequest> {
    
        @Override
        public boolean isValid(SomeRequest request, ConstraintValidatorContext context) {
            if (request == null) {
                return true; // Let @NotNull handle null cases
            }
    
            boolean isValid = true;
            context.disableDefaultConstraintViolation(); // Prevent default message
    
            if (request.score > 0) {
                // If score is positive, fieldPositive must be non-empty, and fieldZeroAndNegative must be null
                if (request.fieldPositive == null || request.fieldPositive.isEmpty()) {
                    isValid = false;
                    context.buildConstraintViolationWithTemplate("fieldPositive must not be empty when score is positive")
                            .addPropertyNode("fieldPositive")
                            .addConstraintViolation();
                }
                if (request.fieldZeroAndNegative != null) {
                    isValid = false;
                    context.buildConstraintViolationWithTemplate("fieldZeroAndNegative must be null when score is positive")
                            .addPropertyNode("fieldZeroAndNegative")
                            .addConstraintViolation();
                }
            } else {
                // If score is zero or negative, fieldZeroAndNegative must be non-empty, and fieldPositive must be null
                if (request.fieldZeroAndNegative == null || request.fieldZeroAndNegative.isEmpty()) {
                    isValid = false;
                    context.buildConstraintViolationWithTemplate("fieldZeroAndNegative must not be empty when score is zero or negative")
                            .addPropertyNode("fieldZeroAndNegative")
                            .addConstraintViolation();
                }
                if (request.fieldPositive != null) {
                    isValid = false;
                    context.buildConstraintViolationWithTemplate("fieldPositive must be null when score is zero or negative")
                            .addPropertyNode("fieldPositive")
                            .addConstraintViolation();
                }
            }
    
            return isValid;
        }
    }
    
    1. Aplicar a anotação ao registro

    Modifique SomeRequestpara usar a nova anotação de validação:

    @ValidSomeRequest
    public record SomeRequest(int score, String fieldPositive, String fieldZeroAndNegative) { }
    
    1. Validar no Controlador

    O Spring Boot validará automaticamente SomeRequest usando a anotação ao manipular a solicitação:

    import jakarta.validation.Valid;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    class FieldValidationController {
    
        @PostMapping("/validate")
        String validate(@Valid @RequestBody SomeRequest someRequest) {
            return "Request is valid";
        }
    
    }
    

    Por que isso funciona

    • A validação é realizada no nível da classe, então todos os campos são acessíveis.
    • A lógica de validação verifica ambos os scorecampos e os campos dependentes em um só lugar.
    • Mensagens de erro personalizadas são atribuídas a campos específicos usando addPropertyNode(), melhorando a clareza nas respostas da API.

    Essa abordagem garante que sua solicitação seja validada corretamente com base na pontuação, sem a necessidade de anotações separadas para cada campo.

    • 1

relate perguntas

  • Lock Condition.notify está lançando java.lang.IllegalMonitorStateException

  • Resposta de microsserviço Muitos para Um não aparece no carteiro

  • Validação personalizada do SpringBoot Bean

  • Os soquetes Java são FIFO?

  • Por que não é possível / desencorajado definir um lado do servidor de tempo limite de solicitação?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve