How to format Spring REST Docs in response body with mockMvc - java

I write my API documentation with Spring REST Docs.
Code example:
#Override
public void getById(String urlTemplate, PathParametersSnippet pathParametersSnippet, Object... urlVariables) throws Exception {
resultActions = mockMvc.perform(get(urlTemplate, urlVariables)
.principal(principal)
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
// do..
}
But the problem is that the result of the test is answered in one line. And understanding the structure of the returned data is very difficult.
Response example:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"creator":null,"modifier":null,"modificationTime":null,"creationTime":null,"id":100,"deleted":false,"name":"Name","description":null,"report":[{"creator":"System","modifier":"System","modificationTime":"2019-01-30T14:21:50","creationTime":"2019-01-30T14:21:50","id":1,"name":"Form name","reportType":{"creator":"System","modifier":"System","modificationTime":"2019-01-30T14:21:50","creationTime":"2019-01-30T14:21:50","id":1,"deleted":false,"name":"Raport"},"unmodifiable":true}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
Further, I generate documentation from the answer received and in the documentation also unformatted JSON
What am I doing wrong? How to enable formatting for json?

If you're not in a position to configure your application to produce pretty-printed responses, you can have REST Docs do it for you prior to them being documented. This is described in the Customizing Requests and Responses section of the documentation:
Preprocessing is configured by calling document with an OperationRequestPreprocessor, and/or an OperationResponsePreprocessor. Instances can be obtained using the static preprocessRequest and preprocessResponse methods on Preprocessors. For example:
this.mockMvc.perform(get("/")).andExpect(status().isOk())
.andDo(document("index", preprocessRequest(removeHeaders("Foo")),
preprocessResponse(prettyPrint())));
In the case above the request is being preprocessed to remove a Foo header and the response is being preprocessed so that it appears pretty-printed.

You can try get ResultActions object from mockMvc and than get MockHttpServletResponse object. After that you can get all the values of the fields that came in response. In this case, you will not need to parse the string.
resultActions = mockMvc.perform(get(urlTemplate, urlVariables)
.principal(principal)
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
MockHttpServletResponse content = resultActions.andReturn().getResponse();
Also you can transform MockHttpServletResponse object data to json. IUf you use Jacson, than write your custom serializer for this object, add it to MockHttpServletResponse and register in ObjectMapper.
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(MockHttpServletResponse.class, CustomSerializer.class);
mapper.registerModule(module);
String jsonResult = mapper.writeValueAsString(content);
CustomSerializer should extends StdSerializer<MockHttpServletResponse> and override serialize method.

Related

Spring Boot 2.5.0 - REST controller, MockMvc not UTF-8

In my REST controller I use
#PostMapping, #GetMapping, etc. without any other specification.
The default must be therefore JSON, for example for #GetMapping. Also there is no specification of the character encoding, it must be UTF-8 I assume, I couldn't finde the default character encoding in the documentation.
However in my tests I use MockMvc.
When I do a POST request, it looks like this:
public static MvcResult performPost(MockMvc mockMvc, String endpoint, String payload, ResultMatcher status) throws Exception {
MvcResult mvcResult = mockMvc.perform(
post(endpoint)
.content(payload)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andDo(print())
.andExpect(status)
.andReturn();
return mvcResult;
}
Question:
The .andDo(print()) part seems not to use UTF-8. How to fix this? Some characters like the german 'ü' are not printed correctly in the console of my NetBeans IDE. It looks like (see Body):
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = application/json
Body = {"Tür"}
Forwarded URL = null
Redirected URL = null
Cookies = []
Question:
When my method returns MvcResult, I can do:
MockHttpServletResponse response = mvcResult.getResponse();
ObjectMapper objectMapper = new ObjectMapper();
String contentAsString = response.getContentAsString(StandardCharsets.UTF_8);
I figured out, that I have to use StandardCharset.UTF_8 to obtain the correct characters, like 'ü'.
But why is in MockHttpServletResponse response the characterEncoding ISO-8859-1? Where does ISO-8859-1 come from, where is this set? Can it be changed to UTF-8?
When I instead try:
String contentAsString = response.getContentAsString(StandardCharsets.ISO_8859_1);
I don't get the german 'ü', the String value is "Tür". Although in ISO_8859_1 according to https://en.wikipedia.org/wiki/ISO/IEC_8859-1 the character 'ü' is in the Code page layout table.
Yes, this is definitely awkward
Class: repository\org\springframework\spring-test\5.3.7\spring-test-5.3.7.jar!\org\springframework\test\web\servlet\result\MockMvcResultHandlers.class
Method:
public static ResultHandler print() {
return print(System.out);
}
Method:
public static ResultHandler print(OutputStream stream) {
return new PrintWriterPrintingResultHandler(new PrintWriter(stream, true));
}
Constructor:
public PrintWriter(OutputStream out, boolean autoFlush) {
this(out, autoFlush, Charset.defaultCharset());
}
the culprit, as I understand, is Charset.defaultCharset(), should be UTF-8
This answer to a related question shows how to set the default encoding for all tests (indeed the documentation does not specify what is the default).
If you don't want to rely on (yet another) configuration item set up outside the tests, I believe setting the encoding in the request will automatically make MockMvc do the right thing. We do that in our tests where JSON payloads carry accented characters.
MvcResult mvcResult = mockMvc.perform(
post(endpoint)
.content(payload)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.characterEncoding("utf-8"))
.andDo(print())
.andExpect(status)
.andReturn();

Append Object property to RestEasy InputStream containerRequestContext

I'm trying to add the principal id to every incoming request as part of the body. This is the code I am using:
InputStream in = containerRequestContext.getEntityStream();
String jsonRequestString = IOUtils.toString(in, encoding);
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonParser parser = factory.createParser(jsonRequestString);
JsonNode jsonNode = mapper.readTree(parser);
((ObjectNode) jsonNode).put(PRINCIPAL_ID, containerRequestContext.getSecurityContext().getUserPrincipal().getName());
containerRequestContext
.setEntityStream(IOUtils.toInputStream(mapper.writeValueAsString(jsonNode), encoding));
This is my resource:
#POST
#Path("/service")
#RolesAllowed(USER_ROLE)
#Produces(MediaType.APPLICATION_JSON)
public Response myService(String principalId, String input){
// do stuff
}
When request hits the resource, I can see the new json string being bind on principalId argument e.g.
principalId = {"principalId": "id", "input":"input"}
is there a way to bind each property to the respective argument?
You have two options here:
write interceptor (with AspectJ, for instance) which handle all your endpoints and populates principalId to your method's parameter or POJO.
write a servlet filter. This option is required to read the same request twice. The first read will occur in the filter with your following preprocessing and the second one in the Spring servlet.

CORS response when using objectWritter as written type - Jersey

I am using Jersey service and sending a json response from a service call. Now my service will be used from cross domain as well. so i want to implement CORS in my code. I saw few examples in internet but all are returning response object. But in my code i am returning as ObjectWritter. in this case how to make this as CORS.
My code as below
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/sampleUrl/{pageNumber}")
public String fetchAlertRecords(#PathParam("pageNumber") int pageNumber) throws Exception {
List<CustomVO> list = new ArrayList<CustomVO>();
//do somethimg to insert values to list
ObjectWriter writer = null;
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
FilterProvider filters = new SimpleFilterProvider()
.addFilter(filterClass,SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
writer = mapper.writer(filters);
return writer.writeValueAsString(list);
}
CORS is about request/response headers, what you return (btw you return String and not ObjectWriter) is a response body. So use CORS as you have seen it in examples. May be this link will help https://spring.io/guides/gs/rest-service-cors/

resttemplate getForObject map responsetype

Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I'm invoking a rest service that returns JSON like this:
{
"some.key" : "some value",
"another.key" : "another value"
}
I would like to think that I can invoke this service with a java.util.Map as the response type but that's not working for me. I get this exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map]
Should I just specify String as the response type and convert the JSON to a Map?
Edit I
Here's my restTemplate call:
private Map<String, String> getBuildInfo(String buildUrl) {
return restTemplate.getForObject(buildUrl, Map.class);
}
Here's how I'm setting up the restTemplate:
#PostConstruct
public void initialize() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
requestWrapper.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return execution.execute(requestWrapper, body);
}
});
restTemplate.setInterceptors(interceptors);
}
Edit II
Full error message:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at idexx.ordering.services.AwsServersServiceImpl.getBuildInfo(AwsServersServiceImpl.java:96) ~[classes/:na]
RestTemplate has a method named exchange that takes an instance of ParameterizedTypeReference as parameter.
To make a GET request that returns a java.util.Map, just create an instance of an anonym class that inherits from ParameterizedTypeReference.
ParameterizedTypeReference<Map<String, String>> responseType =
new ParameterizedTypeReference<>() {};
You can then invoke the exchange method:
RequestEntity<Void> request = RequestEntity.get("http://example.com/foo")
.accept(MediaType.APPLICATION_JSON).build();
Map<String, String> jsonDictionary = restTemplate.exchange(request, responseType).getBody();
As I had previously noted, your error message is showing us that you are receiving application/octet-stream as a Content-Type.
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
As such, Jackson's MappingJackson2HttpMessageConverter cannot parse the content (it's expecting application/json).
Original answer:
Assuming your HTTP response's Content-Type is application/json and you have have Jackson 1 or 2 on the classpath, a RestTemplate can deserialize JSON like you have into a java.util.Map just fine.
With the error you are getting, which you haven't shown in full, either you've registered custom HttpMessageConverter objects which overwrite the defaults ones, or you don't have Jackson on your classpath and the MappingJackson2HttpMessageConverter isn't registered (which would do the deserialization) or you aren't receiving application/json.
I think you can achieve what you're aiming for simply using the RestTemplate and specifying a JsonNode as the response type.
ResponseEntity<JsonNode> response =
restTemplate.exchange(url, HttpMethod.GET, entity, JsonNode.class);
JsonNode map = response.getBody();
String someValue = map.get("someValue").asText();
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I ended up getting the contents as a String and then converting them to a Map like this:
String json = restTemplate.getForObject(buildUrl, String.class);
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
try {
//convert JSON string to Map
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
} catch (Exception e) {
logger.info("Exception converting {} to map", json, e);
}
return map;
I know its old, but just for other people that may visit this topic:
If you want to register some additional converters with RestTemplateBuilder you also have to explicitly register default ones
#Bean
public RestTemplateBuilder builder() {
return new RestTemplateBuilder()
.defaultMessageConverters()
.additionalMessageConverters(halMessageConverter());
}
private HttpMessageConverter halMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jackson2HalModule());
TypeConstrainedMappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
halConverter.setObjectMapper(objectMapper);
return halConverter;
}
This worked 100% for me
in client
Map<String, Object> mapRespuesta = new HashMap<>();
mapRespuesta.put("mensaje", "Process completed successfully");
mapRespuesta.put("idResponse", id);
return new ResponseEntity<Map<String, Object>>(mapRespuesta, HttpStatus.OK);
in which it makes the connection
ResponseEntity<Map> result = restTemplate.postForEntity(url, params, Map.class);
String id = result.getBody().get("idResponse").toString();
#GetMapping(value = "getSunny/{userId}")
public Map<String, SunnyVO> getSunny(#PathVariable int sunnyId) {
Map<String, SunnyVO> newObj = new HashMap<String, SunnyVO>();
final String url = "http://localhost:8085/Sunny/getSunny/{sunnyId}";
RestTemplate restTemplate = new RestTemplate();
newObj = restTemplate.getForObject(url, Map.class, sunnyId);
return newObj;
}
It is working for me ...

Trying to use bing translator API with Robospice in Android

I need to implement robospice for doing the networking part in my Translator app. I previously used async task class and it was working fine, but now i want to improve my application with implementing robospice. I'am trying to execute the following code but it doesn't't throw any exception it just never executes....
#Override
public TranslatedText loadDataFromNetwork() throws Exception {
String jsonString = getJsonString();
String headerValue = getHeaderValue(jsonString);
String text = pair.getWordPairs().getWordFrom();
String languageFrom = pair.getLanguagePairs().getLanguageFrom().getCode();
String languageTo = pair.getLanguagePairs().getLangougateTo().getCode();
String uri = String
.format("http://api.microsofttranslator.com/v2/Http.svc/Translate?text=%s&from=%s&to=%s&contentType=text/html",
URLEncoder.encode(text, "UTF-8"),
URLEncoder.encode(languageFrom, "UTF-8"),
URLEncoder.encode(languageTo, "UTF-8"));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", headerValue);
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the Simple XML message converter
getRestTemplate().getMessageConverters().add(new SimpleXmlHttpMessageConverter());
//set the headerValue in the Entity
org.springframework.http.HttpEntity<?> request = new org.springframework.http.HttpEntity<Object>(headerValue);
// Make the HTTP GET request, marshaling the response from XML to an
// EventList
Log.v("request","Making request!");
//This line never finish execuitng, doesen't throw exception or anything in logCat
ResponseEntity<Object> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, null);
Log.v("request", responseEntity.getBody().toString());
Log.d("Load Data From Network", request.getBody().toString());
return null;
}
The last thing it shows in log cat is Request First!! And nothing after that. It never even gets to The Request Listener onRequestFailure.
Can any 1 tell me what i do wrong ?
This is what look weird to me in your code:
ResponseEntity<Object> and null as 4th parameter of the exchange method are not correct. You need to provide a type which represents the response you get from the server.
The object returned by loadDataFromNetwork() is what you will get in the onRequestSuccess() method. Returning null is not a good idea, in my opinion.
I fixed the problem. So if you need to handle stream you will have to provide the following code
ResponseEntity<byte[]> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, byte[]);

Categories