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

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();

Related

Can't figure out how to change Prometheus content type header

So my metrics all appear in one line at my end-point, not in new line per metric.
I use micrometer, spring, prometheus and scala.
My controller:
#RequestMapping(Array(""))
class MetricsController #Inject() (prometheusRegistry: PrometheusMeterRegistry) {
#RequestMapping(value = Array("/metrics"), method = Array(RequestMethod.GET))
def metricses(): String = {
prometheusRegistry.scrape()
}
}
Should it be enough to change the way I write the metrics them selves?
I have tried to add scrape(TextFormat.CONTENT_TYPE_004) but that changed nothing.
Does it have to do with the HTTP response header?
Would it work to add:
.putHeader(HttpHeaders.CONTENT_TYPE, TextFormat.CONTENT_TYPE_004)
.end(registry.scrape());
If so how would I do that in my case?
Thanks
Prometheus (or other compatible backends) will send you an Accept header that you should not ignore (please read about content negotiation) but if you want to ignore it:
#GetMapping(path = "/metrics", produces = MediaType.TEXT_PLAIN_VALUE)
#ResponseBody String metrics() {
return registry.scrape();
}
If you don't want to ignore it, TextFormat has a chooseContentType method that you can utilize to get the content type based on the Accept header:
#GetMapping(path = "/metrics")
#ResponseBody ResponseEntity<String> metrics(#RequestHeader("accept") String accept) {
String contentType = TextFormat.chooseContentType(accept);
return ResponseEntity
.ok()
.contentType(MediaType.valueOf(contentType))
.body(registry.scrape(contentType));
}
Or you can also set-up content negotiation: https://www.baeldung.com/spring-mvc-content-negotiation-json-xml

Spring controller adding charset to content type header for image return method

I am writing a simple Spring controller endpoint to return an image from DB, so the image I have is a raw byte array, the code I am using:
#RequestMapping(value = "/getphoto", method = RequestMethod.GET , produces = MediaType.IMAGE_GIF_VALUE)
public ResponseEntity<Resource> getphoto(#RequestParam(#RequestParam("uuid") UUID uuid) {
byte[] image = service.getPhoto(uuid);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
headers.setContentLength(image.length);
return new ResponseEntity<Resource>(new ByteArrayResource(image), headers, HttpStatus.OK);
}
The problem is that I am getting the image bytes but the content type is always appended with charset like this image/jpeg;charset=UTF-8 how can I remove that charset for non text mime type? I have tried to add the content type using the strings method headers.set("Content-Type", "image/jpeg"); but still no joy! any suggestions? I believe this charset is preventing lots of testing clients to display the image, like postman in chrome and restclinet in firefox.
Edit
I have managed to remove the charset in many ways, but it looks this is not the only problem in the way I am doing this endpoint, in restclinet I am getting:
Cannot preview image
Your response is an image, but we need to override the mime type to
preview this image. Would you like to override the mime type to
"text/xml; charset=x-user-defined" and re-send this request?
when I click yes please continue, the image can be seen in restclient,
in postman in chrome, the image is still not displayed as well.
Use like this,
#RequestMapping(value = "/getphoto", method = RequestMethod.GET , produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<?> getphoto(#RequestParam(#RequestParam("uuid") UUID uuid) {
byte[] image = service.getPhoto(uuid);
ResponseEntity<?> responseEntity = null;
if(image == null)
responseEntity = ResponseEntity.notFound().build();
else
responseEntity = ResponseEntity.ok(image);
return responseEntity;
}
This will put the byte array directly into response as #Henry suggested. And respond with 404 if image[] is null.
It may be that you are you are using the CharacterEncodingFilter with forceEncoding via spring boot, beans or web.xml. That's what I had.

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[]);

Can you set the src attribute of an HTML image tag to a controller method?

I know that you can do this
<img src="http://some_svg_on_the_web" />
But what if I have a controller method annotated with #ResponseBody
#RequestMapping(value="getSVG")
public #ResponseBody String getSVG(HttpServletRequest request, HttpServerletResponse response) {
String SVG = // build the SVG XML as a string
return SVG;
}
Can I then say
<img src="/getSVG" />
I have tested and the controller is definitely being hit but no image is being shown on the page.
I believe the problem is Spring is setting the default content type to application/octet-stream and the browser can't read your XML as that. Instead, you need to actually set the Content-Type header, either through the HttpServerletResponse or with Spring's ResponseEntity.
#RequestMapping(value="getSVG")
public #ResponseBody ResponseEntity<String> getSVG(HttpServletRequest request, HttpServerletResponse response) {
String SVG = // build the SVG XML as a string
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("image/svg+xml"));
ResponseEntity<String> svgEntity = new ResponseEntity<String>(svg, headers, HttpStatus.OK);
return svgEntity;
}
The fact that the you have the XML as a String doesn't really matter, you could've used getBytes() to make the content byte[]. You can also use the Resource class to have Spring get the bytes directly from a classpath or file system resource. You would parameterize ResponseEntity accordingly (there are a number of supported types).

How to format Spring REST Docs in response body with mockMvc

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.

Categories