How to use Gson instead of Jackson for MockMvc? - java

I have spent the past day trying to find a solution to this and could not find any online resource that solves this.
I am using Gson for Message conversion for my application, which works fine outside of unit testing. I even added a HttpMessageConverters bean to take precedence over Jackson instead of writing config values to application.yml. This only works when running the application.
Now my question is, how do I use the Gson serialization/deserialization for MockMvc? I have a class with the #SerializedName("field_one") annotation which the value differs from the actual name. The closest I got to finding an answer was the one below which didn't help:
https://stackoverflow.com/a/20510028/3948882
Any ideas how to replace the ObjectMapper or have MockMvc use Gson instead of Jackson?
Edit: To add a little more context:
When I try to send a Model which was converted to Json with Gson, it get's immediately refused (400) because I have #NotNull annotation for each field in the model. When it deserializes in the controller, it sets fields to null. The below example has #Valid, which makes sure the Model checks out.
#RequestMapping(value = "accept", method = RequestMethod.POST)
public Model resp(#Valid #RequestBody Model model){
return model;
}
On the flip side, when I go to hit an endpoint without #Valid, passing a json that pleases Jackson, and I get the a model back, I cannot check any of the fields:
mockMvc.perform(
post("/test/accept")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(jsonPath("$.field_one", is("Hello world!")))
Exception:
java.lang.AssertionError: No value at JSON path "$.field_one", exception: No results for path: $['field_one']

You have to set-up the MockMvc correctly:
MockMvc mvc = MockMvcBuilders.standaloneSetup(new YourController())
.setControllerAdvice(new SomeExceptionHandler())
.setMessageConverters(new GsonHttpMessageConverter()) //<-- THIS
.build();

Related

RestTemplate handling inconsistent types List<Object> vs Object

I have a problem with using a RestTemplate while fetching an API which has inconsistent data types of responses.
I wonder if there is a way to make it work without changing the API itself (API is not mine so I can't really change it)
I have a situation when i fetch a list of objects and when it returns not empty it's in format like this:
{
"orders": [
{
...
}
]
}
But when i call this api with limit parameter or something else which returns 0 results then the format of response looks like that
[]
I have an object for serialization/deserialization and in it a list of orders so it works when i got more than 0 results. I don't know if there is a solution already on stack (i didn't found one yet).
Any ideas how to handle this sort of problem?
btw. I already created an issue on github for that API but untill it will be fixed I would like to get it working somehow (any hacks appreciated :) )
Edit:
Http status code is always 200
You can achieve this by using customized objectmapper
public ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
return objectMapper;
}
To make the custom ObjectMapper available to MappingJackson2HttpMessageConverter, simply create a new MappingJackson2HttpMessageConverter and pass in the ObjectMapper instance.
private MappingJackson2HttpMessageConverter createMappingJacksonHttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(createObjectMapper());
return converter;
}
Then, you can create a RestTemplate and add your custom MappingJackson2HttpMessageConverter to its list of message converters.
#Bean
public RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(0,
createMappingJacksonHttpMessageConverter());
}
And then now use this restTemplate to read single object as List
List<Model> models = restTemplate.exchange(uri,POST,entity,
new ParameterizedTypeReference<List<Model>>>(){});
That sounds like a server-side problem rather than a client that reads the info. Try to invoke the same rest API with some browser-based REST client such as Advanced Rest Client (ARC) in chrome or any other client. See if you get the same structure of results for empty and non-empty list. If they are the same as you described above, than it is a server API problem and not your client's problem. So, if that's the case you have to check if your response is [] and convert it manually to { "orders":[]}. If it is not the case, then look into your client or, maybe, use a different HTTP client

Spring boot REST server throws HttpRequestMethodNotSupportedException on POST of unexpected request body with #Validated

Does Spring throw HttpRequestMethodNotSupportedException when a request body is not valid and #Valid (or #Validated) is used? I really expected MethodArgumentNotValidException.
Details: I have a small REST server built on Spring-Boot version 2.2.4. One of the methods looks like this:
#PostMapping("/yapp")
public Yapp kickYapp(#Validated #RequestBody YappDescriptor yappDescriptor) {
logger.debug("kickYapp descriptor {}", yappDescriptor);
doWork(yappDescriptor);
}
The YappDescriptor has annotations like "required" but nothing for valid values, ranges, etc. When I POST a well-formed JSON object with values for all the required fields as defined in the YappDescriptor POJO, the Spring controller method is found and invoked as expected.
I tried a couple error scenarios:
1) If I POST a well-formed JSON object that has only null values for the expected fields, the method is found and entered as expected.
2) If I POST a well-formed JSON object with a key that does not match any of the POJO's fields, the method is NOT found and entered. In watching class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler I see the exception is HttpRequestMethodNotSupportedException and the server answers 405 "Request method 'POST' not supported".
In this controller class, kickYapp is the only POST-mapped method at the specified path, so I think that answer is pretty confusing. Altho I'm definitely sending a bad request (unexpected data), I am surprised my POST-mapped method is not found and invoked.
This post Validating if request body in HTTP POST request is null in Spring Boot controller suggests I should be seeing HttpMessageNotReadableException which would be helpful, but I never get that exception.
Many other questions on SO seem to be about enabling validation of request bodies, like Spring 4.1.7 validate request body , but I seem to be past that.
Thanks in advance for helping me understand this behavior and maybe improve the server to help my users discover their errors more easily (which saves me time :). Thought I could maybe extend that method to accept a BindingResult parameter and report errors that way, but that's a non-starter if the controller method is never entered.
Update to respond to comments: yes I could have used #Valid. In my tests annotation #javax.validation.Valid and #org.springframework.validation.annotation.Validated have the same effect, both turned on validation of the RequestBody parameter.
why not use #Valid?
like so:
public ResponseEntity<SalaryDto> update(#Valid #RequestBody SalaryDto subject)
and don't forget to use javax.validation validation annotations in your request body object

How print json post body with RestTemplate

I need to know, if jackson is doing the correct parse on my class attributes annotated with #JsonProperty, but enabling debug spring options, it does not display the json that was generated in the post.
I tried to create some interceptors to do this task but it did not work, does anyone know if there is any jackson setup that print the payload in the post operation?
just write a unit test for your pojo like this:
#Test
public void testSerialization() throws Exception {
String json = new ObjectMapper().writeValueAsString(yourObjectToBeSerialized);
// and simple print it
System.out.println(json);
// or do some assertions

How to sign JSON message used by Spring Rest controller?

I have a Spring REST application that accepts JSON messages, written like
#RequestMapping(value = "/myhook", method = RequestMethod.POST,
produces = JSON, consumes = JSON)
public #ResponseBody MyResponse doIt
(#Valid #RequestBody(required = true) MyContractRequest request) {
MyResponse response;
...
return response;
}
This works really well with almost no code to support, but now I have a requirement to sign both response and request.
I started from simply computing the shared signature of all message fields at Java level and assigning it to the dedicated signature field. However this requires to have and maintain code for computing the signatures:
public void update(java.security.Signature sign) throws Exception {
sign.update(name);
sign.update(value);
sign.update(etc);
}
Some people around me expressed opinion that the need to write and maintain this signing code may not be the best design, and it may be better to sign the whole message as a single JSON string. I could fetch the request as a string manually, and then process JSON manually, but I really would like to preserve the Spring controller concepts.
Also, I cannot longer have the signature field in the message itself because the value of this field obviously also changes the signature of the JSON string.
Is there any way to compute the signature of the whole JSON message body on the message departure and arrival, and where to place the signature so it could be passed together with the message? One of the idea is to use the custom HTTP header for the signature. Anyway, how to compute it first?
You can use a servlet filter with Spring MVC and modified your content whatever you want in request and response as well
Example :
http://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/
or you can use Spring 3 MVC Interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/

Steps to return json view from Spring controller

My request is ajax based and am calling to one of spring multiaction controller method,I able to create json file using Gson library. Is any way to return json view from controller method.
You can just let your method return the JSON String if you use the #ResponseBody annotation and use one of the methods listed in the answers to my previous question:
In Spring MVC, how can I set the mime
type header when using
#ResponseBody
I'm a total newbie but I've read some where that when you dont specify any logical view mapping for a controller and you return a model map from a handler method (#Controller, #RequestMapping) it should transform the model object to json and return it.

Categories