Response has replaceAll which takes headers(type: MultivaluedMap).
What will be the equivalent code if I want to use ResponseEntity?
Response
.ResponseBuilder
.entity(entityValue)
.replaceAll(headers)
.type(APPLICATION_JSON)
.build();
You can use a constructor with:
new ResponseEntity<>(headers, HttpStatus.OK);
headers is a "MultiValueMap headers".
Related
I’m having an issue with Spring 5 reactive WebClient, when I request an endpoint that returns a correctly formated json response with content type "text/plain;charset=UTF-8".
The exception is
org.springframework.web.reactive.function.UnsupportedMediaTypeException:
Content type 'text/plain;charset=UTF-8' not supported for bodyType=MyDTOClass
Here is how I made the request:
webClient.get().uri(endpoint).retrieve().bodyToFlux(MyDTOClass.class)
EDIT: Headers are "correctly" setted (Accept, Content-Type), I have tried differents content-types (json, json + UTF8, text plain, text plain + UTF8) conbinations, without success. I think the issue is .bodyToFlux(MyDTOClass.class) doesn't know how to translate "text" into MyDTOClass objects.
If I change the request to:
webClient.get().uri(endpoint).retrieve().bodyToFlux(String.class)
I can read the String.
EDIT 2: The next quote is extracted from the Spring documentation
(https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-codecs-jackson)
By default both Jackson2Encoder and Jackson2Decoder do not support
elements of type String. Instead the default assumption is that a
string or a sequence of strings represent serialized JSON content, to
be rendered by the CharSequenceEncoder. If what you need is to render
a JSON array from Flux<String>, use Flux#collectToList() and encode a
Mono<List<String>>.
I think the solution is define a new Decoder/Reader in order to transform the String into MyDTOClass, but i don't know how to do it.
In case someone needs it, here is the solution:
This answer (https://stackoverflow.com/a/57046640/13333357) is the key. We have to add a custom decoder in order to specify what and how deserialize the response.
But we have to keep in mind this: The class level anotation #JsonIgnoreProperties is setted by default to the json mapper and does not have effect to other mappers. So if your DTO doesn't match all the response "json" properties, the deserialization will fail.
Here is how to configure the ObjectMapper and the WebClient to deserialize json objects from text responses:
...
WebClient.builder()
.baseUrl(url)
.exchangeStrategies(ExchangeStrategies.builder().codecs(configurer ->{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
configurer.customCodecs().decoder(new Jackson2JsonDecoder(mapper, MimeTypeUtils.parseMimeType(MediaType.TEXT_PLAIN_VALUE)));
}).build())
.build();
...
Cheers!
Following up on this answer from Charlie above, you can now add an extra "codec" without replacing them.
You can also easily build Spring's default configured ObjectMapper via Jackson2ObjectMapperBuilder.json().build()
Here's an example that reuses the ObjectMapper from the built-in Jackson2JsonDecoder
var webClient = webClientBuilder
.baseUrl(properties.getBaseUrl())
.codecs(configurer -> {
// This API returns JSON with content type text/plain, so need to register a custom
// decoder to deserialize this response via Jackson
// Get existing decoder's ObjectMapper if available, or create new one
ObjectMapper objectMapper = configurer.getReaders().stream()
.filter(reader -> reader instanceof Jackson2JsonDecoder)
.map(reader -> (Jackson2JsonDecoder) reader)
.map(reader -> reader.getObjectMapper())
.findFirst()
.orElseGet(() -> Jackson2ObjectMapperBuilder.json().build());
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(objectMapper, MediaType.TEXT_PLAIN);
configurer.customCodecs().registerWithDefaultConfig(decoder);
})
.build();
Set content type for webclient.
webClient.get()
.uri(endpoint)
.contentType(MediaType.APPLICATION_JSON_UTF8)
I can do this for POST
ResponseEntity<ResponseMessage> response = restTemplate.postForEntity(URL, animal, ResponseMessage.class);
return response.getBody();
But why there is no putForEntity for PUT? There is just resTemplate.put(...
How do I do it for 'PUT' request.
You can use:
restTemplate.exchange(url, HttpMethod.PUT, requestEntity, ...)
Here is the link to the documentation of this method.
You can use PUT instead, reason why putForEntity is not available because as per standard PUT will not return a response body but 201 or 200 in most of the cases.
I am using Sping webflux module and create a WebClient, request uri and request body as follows:
// create webclient
WebClient wc3 = WebClient.builder()
.baseUrl("http://localhost:8080")
.defaultCookie("key", "val")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
// set uri
WebClient.RequestBodySpec uri1 = wc3.method(HttpMethod.POST).uri("/getDocs");
// set a request body
WebClient.RequestBodySpec requestSpec1 = WebClient.create().method(HttpMethod.POST).uri("/getDocs")
.body(BodyInserters.fromPublisher(Mono.just("data")), String.class);
and when i am setting the request body, i am getting the following compilation error:
Multiple markers at this line
- Type mismatch: cannot convert from Mono<String> to P
- The method fromPublisher(P, Class<T>) in the type BodyInserters is not applicable for the arguments
(Mono<String>)
The java editor is showing just "Rename in file" as the suggestion.
I am not sure if i am using the BodyInserters perfectly or not. Please suggest.
It has to be like this
// set a request body
WebClient.RequestHeadersSpec<?> data = WebClient.create().method(HttpMethod.POST).uri("/getDocs")
.body(BodyInserters.fromPublisher(Mono.just("data"), String.class));
I don't know why my code is not working, I've tried with Postman and works fine:
But with RestTemplate I can´t get a response while it´s using the same endpoint... .
ResponseEntity<String> responseMS = template.exchange(notificationRestService, HttpMethod.DELETE, new HttpEntity<NotificationRestDTO[]>(arrNotif), String.class);
I've tried with List instead Array[]
When i made a PUT request it´s works fine but with one object:
ResponseEntity<String> responseMS = template.exchange(notificationRestService, HttpMethod.PUT, new HttpEntity<NotificationRestDTO>(notificationDTO), String.class);
Any help?? Thanks!!
From the comments it became clear that you're expecting it to return a 400 Bad Request response. RestTemplate will see these as "client errors" and it will throw a HttpClientErrorException.
If you want to handle cases like this, you should catch this exception, for example:
try {
ResponseEntity<String> responseMS = template.exchange(notificationRestService, HttpMethod.DELETE, new HttpEntity<NotificationRestDTO[]>(arrNotif), String.class);
} catch (HttpClientErrorException ex) {
String message = ex.getResponseBodyAsString();
}
In this case (since you expect a String), you can use the getResponseBodyAsString() method.
The ResponseEntity will only contain the data in case your request can be executed successfully (2xx status code, like 200, 204, ...). So, if you only expect a message to be returned if the request was not successfully, you can actually do what Mouad mentioned in the comments and you can use the delete() method of the RestTemplate.
I am attempting to test a rest service I created. The service is a post.
I wanted to create a file to pass the parameters(including a multi-part file).
From there I am trying to call the service at this point.
Pretty sure the service that doesn't work. But when I call rest Service. I have a simple form that just passes in a couple values including the jpg.
Here is the code.
HttpMessageConverter bufferedIamageHttpMessageConverter = new ByteArrayHttpMessageConverter();
restTemplate.postForObject("http://localhost:8080/sendScreeenAsPostCard", uploadItem.getFileData(), String.class));
My method signature is:
ResultStatus sendScreenAsPostcard( #RequestParam MultipartFile image, #RequestParamString userId)
That is the error I am getting.
Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.commons.CommonsMultipartFile]
You need to simulate a file upload, which requires a particular content type header, body parameters, etc. Something like this should do the trick:
// Fill out the "form"...
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("file", new FileSystemResource("file.jpg")); // load file into parameter
parameters.add("blah", blah); // some other form field
// Set the headers...
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "multipart/form-data"); // we are sending a form
headers.set("Accept", "text/plain"); // looks like you want a string back
// Fire!
String result = restTemplate.exchange(
"http://localhost:8080/sendScreeenAsPostCard",
HttpMethod.POST,
new HttpEntity<MultiValueMap<String, Object>>(parameters, headers),
String.class
).getBody();