Saltar al contenido

Spring Boot: Validación personalizada en parámetros de solicitud

Esta es la contestación más exacta que te podemos dar, pero mírala pausadamente y valora si se adapta a tu trabajo.

Solución:

Tendría que cambiar algunas cosas para que esta validación funcione.

El controlador debe anotarse con @Validated y @ValuesAllowed debe anotar el parámetro de destino en el método.

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Validated
@RestController
@RequestMapping("/api/opportunity")
public class OpportunityController 

    @GetMapping("/vendors/list")
    public String getVendorpage(
            @RequestParam(required = false)
            @ValuesAllowed(values = 
                    "OpportunityCount",
                    "OpportunityPublishedCount",
                    "ApplicationCount",
                    "ApplicationsApprovedCount"
            ) String orderBy,
            @RequestParam(required = false) String term,
            @RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
            @RequestParam(required = false) String sortDir) 
        return "OK";
    

@ValuesAllowed debería apuntar ElementType.PARAMETER y en este caso ya no necesitas propName propiedad porque Spring validará el parámetro deseado.

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValuesAllowedValidator.class)
public @interface ValuesAllowed 

    String message() default "Field value should be from list of ";
    Class[] groups() default ;
    Class[] payload() default ;

    String[] values();

Validador:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;

public class ValuesAllowedValidator implements ConstraintValidator 

    private List expectedValues;
    private String returnMessage;

    @Override
    public void initialize(ValuesAllowed requiredIfChecked) 
        expectedValues = Arrays.asList(requiredIfChecked.values());
        returnMessage = requiredIfChecked.message().concat(expectedValues.toString());
    

    @Override
    public boolean isValid(String testValue, ConstraintValidatorContext context) 
        boolean valid = expectedValues.contains(testValue);

        if (!valid) 
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(returnMessage)
                    .addConstraintViolation();
        
        return valid;
    

Pero el código anterior devuelve HTTP 500 y contamina los registros con un seguimiento de pila feo. Para evitarlo, puede poner tales @ExceptionHandler en el cuerpo del controlador (por lo que se limitará solo a este controlador) y obtendrá control sobre el estado HTTP:

@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
String handleConstraintViolationException(ConstraintViolationException e) 
    return "Validation error: " + e.getMessage();

… o puede poner este método por separado @ControllerAdvice class y tener aún más control sobre esta validación, como usarla en todos los controladores o solo en los deseados.

Caso 1: Si la anotación ValuesAllowed no se activa en absoluto, podría deberse a que no se anotó el controlador con @Validated.

@Validated
@ValuesAllowed(propName = "orderBy", values =  "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount", "ApplicationsApprovedCount" )
public class OpportunityController {
@GetMapping("/vendors/list")
public String getVendorpage(@RequestParam(required = false) String term,..

Caso 2: Si se activa y arroja un error, podría deberse a la BeanUtils.getProperty no resolver las propiedades y lanzar excepciones.

Si las soluciones anteriores no funcionan, puede intentar mover la anotación al nivel del método y actualizar el Validador para usar la lista de valores válidos para el OrderBy parámetro. Esto funcionó para mí. A continuación se muestra el código de muestra.

@RestController
@RequestMapping("/api/opportunity")
@Validated
public class OpportunityController {
    @GetMapping("/vendors/list")
    public String getVendorpage(@RequestParam(required = false) String term,
            @RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
            @ValuesAllowed(propName = "orderBy", values =  "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount",
                    "ApplicationsApprovedCount" ) @RequestParam(required = false) String orderBy, @RequestParam(required = false) String sortDir) 
        return "success";
    
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy =  ValuesAllowed.Validator.class )
public @interface ValuesAllowed 

    String message() default "Field value should be from list of ";

    Class[] groups() default ;

    Class[] payload() default ;

    String propName();

    String[] values();

    class Validator implements ConstraintValidator 
        private String propName;
        private String message;
        private List allowable;

        @Override
        public void initialize(ValuesAllowed requiredIfChecked) 
            this.propName = requiredIfChecked.propName();
            this.message = requiredIfChecked.message();
            this.allowable = Arrays.asList(requiredIfChecked.values());
        

        public boolean isValid(String value, ConstraintValidatorContext context) 
    

Aquí tienes las comentarios y calificaciones

Recuerda recomendar esta sección si te valió la pena.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *