I have this controller :
#GetMapping(value = "/stream")
public Flux<DataBuffer> stream() {
...
return webClient.get().uri(builder.buildAndExpand(parametres).toUriString())
.header(HttpHeaders.CONTENT_TYPE, TEXT_CSV).retrieve().bodyToFlux(DataBuffer.class);
}
}
When i called this endpoint with PostMan, i have this reponse :
[
{
"nativeBuffer": {
"direct": true,
"readOnly": false,
"contiguous": true,
"readable": true,
"writable": false
},
"allocated": true
},
...
My question is, how can i consume this endoint with webclient to read the content of flux. Or perhaps, the controller is wrong ?
I tried this, but it's don't work :
Flux<DataBuffer> fluxDataBuffer= webClient.get()
.uri("http://localhost:8080/stream")
.retrieve()
.bodyToFlux(DataBuffer.class);
DataBufferUtils.write(fluxDataBuffer, Paths.get("test.txt"),
StandardOpenOption.CREATE)
.share().block();
Thanks for your help
Related
getUserDetails Method returns Mono of Type JsonNode. But I Actually want to return a Mono<User.java> or Flux<User.java>. please help modifying getBulkUserInfo or getUserDetails to get the Mono<User.java> or Flux<User.java>
public Mono<JsonNode> getUser(BigInteger Id){
return this.client.get()
.uri("/URL/{Id}",Id)
.retrieve()
.bodyToMono(JsonNode.class);
}
public Flux getBulkUsers(List<BigInteger> Ids){
return Flux.fromIterable(Ids).flatMap(this::getUser);
}
But The json response from the Url is something like
{
"resultholder": {
"totalResults": "1",
"profiles": {
"profileholder": {
"user": {
"country": "IND",
"zipCode": "560048",
"name":"Test"
}
}
}
}
}
I tried different ways but nothing worked subscribe() and
.doOnNext(resp -> resp.get("resultholder").get("profiles").get("profileholder").get("user"))
.bodyToMono(JsonNode.class)
.doOnNext(resp ->{return
JSONUtils.deserialize(resp.get("resultholder").get("profiles").get("profileholder").get("user"), User.class)})
This is pretty straightforward and there is no need to block. Its just applying further mappings on the response. You can use the following code for your problem
return webClient
.get()
.uri("profilesEndPoint/" + id)
.retrieve()
.bodyToMono(JsonNode.class)
.map(jsonNode ->
jsonNode.path("resultholder").path("profiles").path("profileholder").path("user")
).map(
userjsonNode -> mapper.convertValue(userjsonNode, User.class)
);
Where mapper is jackson ObjectMapper
private final ObjectMapper mapper = new ObjectMapper();
If you have any issues please refer to this code here :
I'm receiving JSON from REST API looks like:
{
"items": [
{
"id": 60659,
"name": "Display",
"active": true,
"account_id": 235
},
{
"id": 36397,
"name": " Mail Display",
"active": true,
"account_id": 107
}
]
}
I'm using this method to parse it:
Mono<List<Item>> getItems(String token) {
return webCLient
.get()
.headers(httpHeaders -> httpHeaders.setBearerAuth(token))
.retrieve()
.bodyToMono(ItemResponse.class)
.map(ItemResponse::getResponse)
.retryBackoff(RetrySettings.RETRIES, RetrySettings.FIRST_BACKOFF, RetrySettings.MAX_BACKOFF)
.doOnError(e -> log.error("error: " + e.getCause().toString()))
Response:
public class ItemResponse {
#JsonProperty("items")
private List<Item> response;
}
But sometimes 3rd party API returns different response without top level items property and looks like:
[
{
"id": 60659,
"name": "Display",
"active": true,
"account_id": 235
},
{
"id": 36397,
"name": " Mail Display",
"active": true,
"account_id": 107
}
]
At this point my app is crashing with JSON decoding error. I used for this case:
bodyToMono(new ParameterizedTypeReference<List<Item>>() {})
But I can't always refactoring this part of code just to handle their json. How to do it in dynamical way with Spring WebFlux? Like try -> parse#1 -> catch -> parse#2. So i need to parse json in way#1 and if error occurs app should try to parse it with way#2.
You can get the response as a string .bodyToMono(String.class) and do whatever you want, with multiple try catches... but I think your best bet is to create a custom Deserializer and use it with your WebClient via ExchangeStrategies like described here: How to customize SpringWebFlux WebClient JSON deserialization?
.
class MyResponse {
List<Object> data;
MyResponse(List<Object> data) {
this.data = data;
}
}
class MyResponseDeserializer extends JsonDeserializer<MyResponse> {
#Override
public MyResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
List<Object> data = new ArrayList<>();
if (treeNode.isArray()) {
// parse it as array
} else {
// parse it as object and put inside list
}
MyResponse myResponse = new MyResponse(data);
return myResponse;
}
}
And then
WebClient getWebClient() {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(MyResponse.class, new MyResponseDeserializer());
objectMapper.registerModule(simpleModule);
ExchangeStrategies strategies = ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
}).build();
return WebClient.builder().exchangeStrategies(strategies).build();
}
Mono<List<Item>> getItems(String token) {
return getWebClient()
.get()
.headers(httpHeaders -> httpHeaders.setBearerAuth(token))
.retrieve()
.bodyToMono(MyResponse.class)
.map(MyResponse::data)
.retryBackoff(RetrySettings.RETRIES, RetrySettings.FIRST_BACKOFF, RetrySettings.MAX_BACKOFF)
.doOnError(e -> log.error("error: " + e.getCause().toString()))
}
The rest is the same as in your example just change the class name and add appropriate fields.
And of course this is just a fast written demo and everything hardcoded and within a one method, better to have them injected
I am a newbie in WebClient. I want to consume a rest service and replace some value and return the result.
this is the response I get from rest service:
[
{
"type": "somthing",
"details": {
"d1": "va1",
"d2": "va2",
"d3": "va3"
}
},
{
.....
},
...
]
This is the result I want to return to the user. (the new value is something that I get from the user, so I have it as a parameter.)
[
{
"type": "somthing",
"details": {
"d1": "va1",
"d2": "va2",
**"d3": "Replace with new value"**
}
},
{
.....
},
...
]
Flux<Item> items= webClient.get()
.uri("------URL------")
.retrieve()
.bodyToFlux(Item.class)
Above code correctly return items from the rest service and traditionally I can use collectList().block() to get a List and replace the value inside the object and return it.
I feel that it is an old-fashion way. Is there any better way to handle this situation by using WebClient functionality?
Thanks to #michalk I used map and it worked.
Flux<Item> items= webClient.get()
.uri("------URL------")
.retrieve()
.bodyToFlux(Item.class)..map(item-> {
item.getDetails().setDThree(request.getValue);
return item;
});
I received empty data from spring controller, even if it returned data the ajax success function received it empty. I tried to return string directly from controller like this:
#ResponseBody
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#RequestParam("posImsi") String posImsi,#RequestParam("posMsisdn") String posMsisdn){
return "success";
}
and the ajax is:
$.ajax({
url : "test",
type : "POST",
data : formData,
beforeSend : function(){
$("#overlay").show();
},
success : function(ajaxResult){
console.log(ajaxResult);
},
complete : function(status) {
},
error : function(jqXHR, status, errorThrown) {
alert(jqXHR);
alert(status);
alert(errorThrown);
}
});
The problem is solved by using the XMLHttpRequest object to do ajax request, I really don't know what is the difference but here is what I used:
var x = new XMLHttpRequest();
x.open("POST","test",true);
x.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
x.send(formData);
x.onload = function () {
console.log(this.responseText);
};
for more information about XMLHttpRequest object: http://www.w3schools.com/xml/dom_http.asp
I am developing Spring MVC 4 Dynamic web module application.
In my application I have simple CRUD operations.
Get requests are working fine but POST and PUT are not working at all.
I am getting this error:
HTTP Status 400 - The request sent by the client was syntactically incorrect.
This is my controller code for GET:
#RequestMapping(value = "/getCustomreById/{id}", method = RequestMethod.GET)
public ResponseEntity<CustomerDetails> getCustomer(
#PathVariable("id") String id) {
System.out.println(id);
if (id != null)
return new ResponseEntity<CustomerDetails>(
serv.getCustomerById(id), HttpStatus.OK);
else
return new ResponseEntity<CustomerDetails>(
serv.getCustomerById("1"), HttpStatus.OK);
}
and for POST :
#RequestMapping(value = "/addCustomer", method = RequestMethod.POST)
public int AddCustomer(#RequestBody CustomerDetails customer) {
return serv.addCustomer(customer);
}
POST Request :
{
"customerName": "Sid",
"customerEmail": "sid#gmail.com",
"customerAddress": [{
"address1": "123 Street",
"address2": " ",
"zipCode": "400065",
"city": "Mumbai",
"state": "Maharashtra",
"country": "India",
"region": "Gateway of India"
}]
}
I read on stackoverflow on this question that I need to add multipart reosolver but even aafter adding that I am getting same error.
Assuming you just need to send int id as response, add #ResponseBody to the method
#RequestMapping(value = "/addCustomer", method = RequestMethod.POST)
#ResponseBody
public int AddCustomer(#RequestBody CustomerDetails customer) {
return serv.addCustomer(customer);
}
Otherwise return ResponseEntity as you are doing for GET
return new ResponseEntity<Integer>(serv.addCustomer(customer), HttpStatus.OK);
Adding #ResponseBody will work for this question