How to pass a plain(not annotated api parameter) object with MockMvc - java

i am trying to pass a regular object to an api, via the MockMvc library.
Here is the example (Partiucalarly the FilterProperties properties object):
API:
#GetMapping("/test-api")
public PageResponse<SomeDto> getAllObjects(
FilterProperties properties,
#RequestParam(value = "searchPhrase", defaultValue = "") String searchPhrase,
#RequestParam(value = "actionType") ActionType actionType) {
System.out.println(searchPhrase);
return null;
}
I managed to successfully pass the #RequestParams with .param("searchPhrase", "SomePhrase"), however i cannot seem to find a way to pass the FilterProperties object, since its just a plain object and is not annotated as param, request body or some sort of attribute.
TEST:
final MvcResult mvcResult = restServiceMockMvc
.perform(get(CONTROLLER_BASE_PATH + "/test-api")
.param("searchPhrase", "SomePhrase")
.param("actionType", String.valueOf(ActionType.EDIT))
.requestAttr("properties", filterProperties)
.contentType(APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
I tried with requestAttr, flashAttr, sessionAttr and it does not break the call, however the api receives an empty object for filterProperties.
I appreciate any insights!

FilterProperties will be treated as there are #ModelAttribute annotated on it (mentioned by the rules in this table).
And #ModelAttribute will enable data binding which will try to bind the value from query parameters , or field name of the form data or others (see this for more details) to FilterProperties .
So that means assuming FilterProperties has the following fields :
public class FilterProperties {
private String prop1;
private String prop2;
}
Then you can configure the MockMvc as
mockMvc.perform(get(CONTROLLER_BASE_PATH + "/test-api")
.param("searchPhrase", "SomePhrase")
.param("actionType", String.valueOf(ActionType.EDIT))
.param("prop1", "prop1-value")
.param("prop2", "prop2-value"))
in order to pass the following values to the fields of the FilterProperties :
prop1 = prop1-value
prop2 = prop2-value

whenever you pass an object to an api, it has to be part of the api signature in one way or another; i guess here you want to pass a body for the GET api(which is highly discouraged),since GET calls are idempotent by nature.
if though if its for testing try passing it as a requestbody and then mock it

Related

how to pass object as a parameter to the mockmvc?

MvcResult mvcResult = mockMvc.perform(post("/user/addItemIntoCart").andExpect(status().isOk()).andReturn();
#PostMapping(value = "/addItemIntoCart",consumes = {"application/json"})
public ResponseEntity<String> addItemToCart(#RequestBody CartDto cartDto) {
return cartService.addItemIntoCart(cartDto);
}
how can I pass object of CartDto to the mockmvc
I am trying to test addItemIntoCart method, but not sure how to pass object to the mockmvc
Here you can use the answer that contains what you want: https://stackoverflow.com/a/35402975/11988137
In a nutshell, you need to create the object and using objectMapper, convert it to a a json string. Then you need to use contentType() and content() method in order to send the json string you've created to the api.

Custom validation for 2 request params in Spring

Is there a way to custom validate 2 of request parameters coming into endpoint in Spring? I would like to be able to validate them with my custom function. Something like add annotation to the request params or on the function where these params are and force these params to be validated by another custom written function.
I need to take both params at the same time, because the validation output of one is dependent on the value of the other one.
I have searched and found some solutions with custom constraint annotations but from what I've read it doesn't seem to solve my problem.
As rightly mentioned, using valiktor is the best option. I have used it in our product as well and it works like a charm.
Below is a snippet example as how you are use it to compare two properties of the same class.
fun isValid(myObj: Myobj): Boolean {
validate(myObj) {
validate(MyObj::prop1).isGreaterThanOrEqualTo(myobj.prop2)
}
Valiktor throws exception with proper message if the validation fails. It also enables you to create custom exception messages if you want to.
Now all you need to do is, create a class for your requestBody and check your conditions with isValid() method explicitly or move it into init block and do it implicitly.
Valiktor has a large number of validations as compared to JSR380, where creating custom validation is a little messy as compared to Valiktor.
If you're going to use the request params to create a POJO, then you can simply use the Javax Validation API.
public class User {
private static final long serialVersionUID = 1167460040423268808L;
#NotBlank(message = "ID cannot be to empty/null")
private int id;
#NotBlank(message = "Group ID cannot be to empty/null")
private String role;
#NotBlank(message = "Email cannot be to empty/null")
private String email;
#NotNull(message = "Password cannot be to null")
private String password;
}
To validate -
#PostMapping("/new")
public String save(#ModelAttribute #Validated User user, BindingResult bindingResult, ModelMap modelMap) throws UnknownHostException {
if (!bindingResult.hasErrors()) {
// Proceed with business logic
} else {
Set<ConstraintViolation<User>> violations = validator.validate(user);
List<String> messages = new ArrayList<>();
if (!violations.isEmpty()) {
violations.stream().forEach(staffConstraintViolation -> messages.add(staffConstraintViolation.getMessageTemplate()));
modelMap.addAttribute("errors", messages);
Collections.sort(messages);
}
return "new~user";
}
}
You can write custom validator by using Validator
Check :: https://docs.spring.io/spring-framework/docs/3.0.0.RC3/reference/html/ch05s02.html
Example :: https://www.baeldung.com/spring-data-rest-validators
valiktor is really good library to validate.
You can do somenthing like:
data class ValidatorClass(val field1: Int, val field2: Int) {
init {
validate(this) {
validate(ValidatorClass::field1).isPositive()
validate(ValidatorClass::field2).isGreaterThan(field1)
}
}
}
make request parameter not required:
#RequestMapping(path = ["/path"])
fun fooEndPoint(#RequestParam("field1", required = false) field1: Int,
#RequestParam("field2", required = false) field2: Int) {
ValidatorClass(field1, field2) //it will throw an exception if validation fail
}
You can handle exception using try-catch or using and ExceptionHandler defined by valiktor.
Using valiktor you can validate fields depending on other fields. You can create one kotlin file where you write all classes that you use to validate fields from requests and in the same way you can use valiktor in you #RequestBody models to validate it.

#RequestParam omitted but mapping is still done correctly

I just found that even if I omit the #RequestParam annotation on the organization parameter, Spring is still able to bind it.
#RequestMapping(value="", method = RequestMethod.POST)
#ResponseBody
public String save(String organization){
logger.info(organization); // it works
}
Can anyone points to the documentation that clarifies this behaviour? I have always though that #RequestParam was mandatory for binding to work.
Thanks
Take a look at https://reversecoding.net/spring-mvc-requestparam-binding-request-parameters/ There is an explanation:
Examples without #RequestParam
Based on the list of
HandlerMethodArgumentResolver configured in your application,
#RequestParam can also be omitted. If you have a look at the code of
method getDefaultArgumentResolvers() of RequestMappingHandlerAdapter
there is the following piece of code at the end:
// Catch-all resolvers.add(new
RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
// Catch-all resolvers.add(new
RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
Basically, it’s added to the resolvers a
RequestParamMethodArgumentResolver with useDefaultResolution set to
true. Looking at the documentation we can see that this means that
method argument that is a simple type, as defined in
BeanUtils.isSimpleProperty(java.lang.Class), is treated as a
request parameter even if it isn’t annotated. The request parameter
name is derived from the method parameter name.
Your resolvers do it automatically. When you pass the HandlerMethodArgumentResolver bean to your resolver, the BeanUtil checks if the parameter is a primitive value or a simple String. If so, it does the binding itself.
#RequestMapping(value = "/rest")
#ResponseBody
public String save(String username, String password) {
return String.format("username=%s password=%s", username, password);
}
Hit the service http://localhost:8080/rest?username=mypwd&password=uname
You will be able to see the result given below.
Output: username=pwd password=uname
here I have some examples for the #RequestParam to provide you,hope they can help you:
#RequestMapping(value = "/selection/findByField", method = RequestMethod.POST)
public #ResponseBody List<selectionsDO> add(#RequestParam(value = "field", required = true) String field,#RequestParam(value = "value", required = true) String value)
{ return mongoService.findByField(field,value);
}
The words "required = true" means that this field must submit at request.

Grab a specific property from JSON payload as MVC method argument

I have asked a similar question before: this one
Now I have a similar but different issue.
My Spring MVC controller model is a JSON payload with a defined set of attributes that, unfortunately, are not part of a class in my project.
E.g.
{
"userId" : "john",
"role" : "admin"
}
I need to treat userId and role as separate Strings.
I currently have two ways to declare the controller method
public ResponseObject mvc(#RequestBody MyCustomDTO dto){
String userId = dto.getUserId();
String role = dto.getRole();
}
public ResponseObject mvc(#RequestBody ModelMap map){
String userId = (String)map.get("userId");
String role = (String)map.get("role");
}
I have been asked to find a different implementation because 1) requires to create a custom DTO class for each combination of parameters (most cases need 1 named parameter, e.g. delete(productId)) and 2) involves an entity that is not strictly defined. Especially when dealing with lists, it can contain arbitrary values that need to be checked at runtime.
Spring MVC, as I have found, does not support resolving #ModelAttribute from a JSON request body. Am I doing something wrong or is it just Spring not doing it? Can I grab a specific property, be it a plain primitive or an entire POJO, from the Request Body into a method argument?
In the second case it would be better to request a useful feature to Spring developers.
Spring version is 4.2.x.
This question is related with the previously linked but differs in the fact that now I will be encapsulating the single property into a Javascript object, so the object that Jackson needs to deserialize won't be a primitive but a POJO.
You won't be able to get individual members as easily, simply because Spring MVC doesn't have any builtin tools to do that. One option is to write your own annotation that describes a parameter at the root of an excepted JSON object body. Then write and register a new HandlerMethodArgumentResolver implementation which processes that annotation on a handler method parameter.
This is not a simple task. Since you can't consume the request content multiple times, you have to save it somehow, in a Filter, for example. For now, let's ignore this restriction and assume we only wanted one parameter. You'd define an annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#interface JsonObjectProperty {
String name();
}
And the HandlerMethodArgumentResolver
class JsonObjectPropertyResolver implements HandlerMethodArgumentResolver {
/**
* Configured as appropriate for the JSON you expect.
*/
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonObjectProperty.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> parameterType = parameter.getParameterType();
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
MediaType contentType = inputMessage.getHeaders().getContentType();
if (!contentType.equals(MediaType.APPLICATION_JSON_UTF8)) {
throw new HttpMessageNotReadableException(
"Could not read document. Expected Content-Type " + MediaType.APPLICATION_JSON_UTF8 + ", was " + contentType + ".");
}
// handle potential exceptions from this as well
ObjectNode rootObject = objectMapper.readValue(inputMessage.getBody(), ObjectNode.class);
if (parameterType == String.class) {
JsonObjectProperty annotation = parameter.getParameterAnnotation(JsonObjectProperty.class);
return rootObject.get(annotation.name()).asText();
}
// handle more
throw new HttpMessageNotReadableException("Could not read document. Parameter type " + parameterType + " not parseable.");
}
}
and finally the handler method
#RequestMapping(value = "/json-new", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String handleJsonProperty(#JsonObjectProperty(name = "userId") String userId) {
String result = userId;
System.out.println(result);
return result;
}
You'll have to register the JsonObjectPropertyResolver appropriately. Once you do, it will be able to extract that JSON property directly into the parameter.
You can use some JSON inline parsers (similar to XML Xpath) where you can provide your JSON string and ask your parser to retrieve some subdocument as String, List or Map. One of the examples is OGNL. It's quite powerful tool, although it is not the only one and not the most performance efficient, but still mature and stable Apache product. So, in your case you would be able feed your JSON string to OGNL and tell it to retrieve properties "userId" and "role" as separate strings. See the OGNL documentation at Apache OGNL page

Spring MVC, deserialize single JSON?

How can I easily separate JSON values that are sent in the same request?
Given that I POST a JSON to my server:
{"first":"A","second":"B"}
If I implement the following method in the Controller:
#RequestMapping(value = "/path", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void handleRequest(#RequestBody String input) {
// ...
}
then the input parameter will constitute a String with the entire JSON object, {"first":"A","second":"B"}. What I really want is two separate Strings (or a String and an int whichever is suitable for the particular request) with just the two values (other key / value pairs that the client may send should be ignored).
If the strings were sent as request parameters instead of JSON request body it would be simple:
#RequestMapping(value = "/path", method = RequestMethod.POST)
public void handleRequest(#RequestParam("first") String first,
#RequestParam("second") String second) {
// ...
}
I know that I can create a simple bean class that can be used in conjunction with the #RequestBody annotation that will contain both A and B when used, but it seems like a detour, since they will have different purposes inside the web app.
Dependencies:
org.springframework : spring-web : 3.1.0.RELEASE
org.codehaus.jackson : jackson-mapper-asl : 1.9.3
POJO
public class Input {
private String first;
private String second;
//getters/setters
}
...and then:
public void handleRequest(#RequestBody Input input)
In this case you need Jackson to be available on the CLASSPATH.
Map
public void handleRequest(#RequestBody Map<String, String> input)
I have written a custom WebArgumentResolver that does exactly this, combined with a custom annotation.
I don't have the source available to me now, but basically I annotated my method like this:
#RequestMapping(value = "/path", method = RequestMethod.POST)
public void handleRequest(#JsonField("first") String first, #JsonField("second") String second) {
// ...
}
Then my JsonFieldWebArgumentResolver checks if the method parameter is annotated with JsonField, and if it is it extracts the actual type from the parameter (not quite straight-forward it turns out if you want to handle generic parameters as well, such as List<String> or List<Pojo>), and invokes Jackson's JsonParser manually to create the correct type. It's a shame I can't show you any code, but that's the gist of it.
However, that solution is for Spring MVC 3.0, and if you are using 3.1 I think you will be better off using a custom HandlerMethodArgumentResolver instead. But the idea should be the same.

Categories

Resources