Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Manual Validation after Argument Resolver

I have a rest controller that looks like this:

@RequestMapping(
        value = "/foo",
        method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<JsonNode> getFOOs(@Valid Payload payload) {
    /** some code **/
}

The Payload class looks like this:

@OneOrTheOther(first = "a", second = "b")
public final class Payload {
    private final String userName;
    private final String a;
    private final String b;
    @NotNull
    private final String c;
    @NotEmtpy{message="At least 1 item"}
    private List<String> names = new ArrayList<String>();
}

And the ArgumentResolver looks like this:

public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().equals(Payload.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        if(supportsParameter(methodParameter)) {
            HttpServletRequest requestHeader = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
            String userName = requestHeader.getHeader("userName");

            ObjectMapper mapper = new ObjectMapper();
            JsonNode requestBody = mapper.readTree(CharStreams.toString(requestHeader.getReader()));

            JsonNode a = requestBody.path("a");
            String a = a.isMissingNode() ? null : a.asText();

            JsonNode b = requestBody.path("b");
            String b = b.isMissingNode() ? null : b.asText();

            JsonNode c = requestBody.path("c");
            String c = c.isMissingNode() ? null : c.asText();

            JavaType type = mapper.getTypeFactory().constructCollectionType(ArrayList.class, String.class);
            List<String> ids
                    = requestBody.path("ids").isMissingNode()
                    ? null : mapper.readValue(requestBody.path("ids").toString(), type);


            return new Payload(username, a, b, c, ids);
        }
        return null;
    }
}

Currently this does about 95% of what I want it to do. It successfully retrieves all the items from the header and request body and creates a Payload object. But after creating the object I want to run the validations annotated in the Payload class like NotNull, NotEmpty or my customer validator OneOrTheOther.

I did some digging around and found a couple stack articles here and here. I don't know how to implement the first one and the second one seems overly complicated and cumbersome so I don't want to really go that route. It seems like using the validateIfApplicable method is the way to go but how would I call it in my context?

like image 761
Richard Avatar asked Jul 22 '16 13:07

Richard


People also ask

What does @validated do?

The @Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class. We'll learn more about how to use it in the section about validating path variables and request parameters.

What is @valid Annotation in spring boot?

The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.

What is @validated in spring boot?

When Spring Boot finds an argument annotated with @Valid, it automatically bootstraps the default JSR 380 implementation — Hibernate Validator — and validates the argument. When the target argument fails to pass the validation, Spring Boot throws a MethodArgumentNotValidException exception.


2 Answers

You could add a SmartValidator instance in your class. Just add:

@Autowired
SmartValidator payloadValidator;

Once injected, you just have to call the validate method on it, passing your bean and the list of errors.

For reference:

SmartValidator: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/SmartValidator.html

Erros: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/Errors.html

like image 144
benjamin.d Avatar answered Oct 15 '22 09:10

benjamin.d


It means you want those validations to be run right after the creation of object. If this is the case then you can create the @Pointcut to the method which returns your Object,and which will call the validations you want @AfterReturning(methodthat returns object)

     @Pointcut("execution(* YourClass.*(..))")
     public void pointcut(){}

     @AfterReturning("method that returns object")
     public final class Payload {
        private final String userName;
        private final String a;
        private final String b;
     }
like image 34
Kintu Barot Avatar answered Oct 15 '22 11:10

Kintu Barot