web-dev-qa-db-fra.com

Validation personnalisée sur le démarrage de printemps RequestHeader

J'ai actuellement ce mappeur de demandes où je suis en train de valider un paramètre de demande à l'aide d'un REGEX.

@RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @RequestHeader(value = "X-API-Key", required = true) String apiKeyHeader) {

     // Stuff here...
 }

Je peux donc valider avec un motif:

@RequestHeader(value = "X-API-Key", required = true) @Valid @Pattern(regexp = SEGMENTS_REGEX) String apiKeyHeader

Mais je voudrais faire une validation personnalisée sur l'attribut d'en-tête i.e.

if (!API_KEY_LIST.contains(apiKeyHeader)) {
    throw Exception();
}
7
ptimson

La meilleure façon de faire cette OMI est de créer une HandlerMethodArgumentResolver personnalisée qui ressemblerait à ceci en utilisant une annotation personnalisée @Segment:

public class SegmentHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(String.class)
            && parameter.getParameterAnnotation(Segment.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String apiKey = webRequest.getHeader("X-API-Key");
        if (apiKey != null) {
            if (!API_KEY_LIST.contains(apiKey)) {
                throw new InvalidApiKeyException();
            }
            return apiKey;
        } else {
            return WebArgumentResolver.UNRESOLVED;
        }
    }
}

Ensuite, la signature de votre contrôleur ressemble à ceci:

@RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @Segment String apiKeyHeader) {

     // Stuff here...
 }

Vous allez enregistrer le résolveur d'arguments de la méthode de gestionnaire dans votre WebMvcConfigurationAdapter:

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(segmentHandler());
    }

    @Bean
    public SegmentHandlerMethodArgumentResolver segmentHandler() {
        return new SegmentHandlerMethodArgumentResolver();
    }

}
7
Ulises

Il y a déjà une demande de fonctionnalité dans le carnet de commandes de Spring, paiement JIRA . Cependant, j'ai pu réaliser ce que vous essayez d'utiliser l'annotation @Validated sur Controller.

@RestController
@RequestMapping("/user")
@Validated
public class UserController {

   @GetMapping("/{loginId}")
   public User getUserBy(@PathVariable @LoginID final String loginId) {
      // return some user
   }
}

Ici, @LoginID est un validateur personnalisé. Et @Validated est de org.springframework.validation.annotation.Validated qui fait l'affaire.

1
Sarvesh Dubey

1) Vérifier manuellement

Vous pouvez injecter HttpServletRequest et vérifier les en-têtes.

@RestController
public class HomeController {
    public ResponseEntity<String> test(HttpServletRequest request){
        if(request.getHeader("apiKeyHeader") == null){
            return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
        }
        return new ResponseEntity<String>(HttpStatus.OK);
    }
}

2) Injecter l'en-tête

@RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity<String> test(@RequestHeader(value="myheader") String myheader){
    return new ResponseEntity<String>(HttpStatus.OK);
}

Cela va retourner:

{
  "timestamp": 1469805110889,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.bind.ServletRequestBindingException",
  "message": "Missing request header 'myheader' for method parameter of type String",
  "path": "/test"
}

si l'en-tête est manquant.

3) Utiliser le filtre

Vous pouvez automatiser la vérification avec certains filtres si vous souhaitez l’utiliser sur plusieurs méthodes. Dans votre filtre personnalisé, récupérez simplement l'en-tête (comme dans la méthode 1) et si l'en-tête est manquant, répondez avec 400 ou ce que vous voulez. Pour moi, cela a du sens lorsque vous n'utilisez pas la valeur d'en-tête dans la méthode du contrôleur et que vous devez simplement valider sa présence.

@Bean
public FilterRegistrationBean someFilterRegistration() { 
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(apiHeaderFilter());
    registration.addUrlPatterns("/example/*");
    registration.setName("apiHeaderFilter");
    registration.setOrder(1);
    return registration;
} 

@Bean(name = "ApiHeaderFilter")
public Filter apiHeaderFilter() {
    return new ApiHeaderFilter();
}

Ignorer la demande

Si vous utilisez l'attribut d'en-tête dans @RequestMapping

@RequestMapping(value = "/test", method = RequestMethod.POST,
    headers = {"content-type=application/json"})

cela aboutira à 404 s'il n'y a pas d'autre gestionnaire pour prendre la demande.

1
Evgeni Dimitrov

Ajoutez simplement la classe suivante . Effectuez toutes les validations dans la méthode "doFilter" et définissez le code de réponse approprié.

@Configuration
public class ApiHeaderFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String token = request.getHeader("token");
        if (StringUtil.isNullOrEmpty(token)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }
}
0
Pruthviraj