Source code in spring which takes in a JSON String(key-value Pair ) as an input and gives out the Updated JSON string - spring

I need help in the coding of a program in spring which takes in a JSON String(key-value Pair ) as an input and gives out the Updated JSON string

I'm assuming you are asking in terms of REST, if yes then refer below snippet -
#Controller
public class JSONTest {
// Below method will take iput as JSON
#RequestMapping(value="/getData",consumes = MediaType.APPLICATION_JSON_VALUE)
public void consumeJson(#RequestBody String s) {
System.out.println("json body : " + s);
}
// Below method will produce output as JSON
#RequestMapping(value="/sendData",produces= MediaType.APPLICATION_JSON_VALUE)
public String sendJson() {
//Create jsonObject
//do some things with jsonObject, put some header information in jsonObject
return jsonObject.toString();
}
}

Related

Array format changes with WebClient Post is used

I am trying to send the below JSON via WebClient to an endpoint. I tried sending it as a POJO, a Map, and a JsonNode for the request body but the array is converted to the index-based format.
JSON to be sent;
{
"jediId":"23",
"name":luke,
"master":"yoda",
"address":[
{
"planet":"tatooine",
"city":"mos eisley"
}
],
"filters":{
"male":1,
"female":1,
"padawan":0,
"master":1
}
}
On the other side, the address is converted to address[0].planet=tatooine, address[0].city=mos eisley i.e the format changes
I created a JsonNode with the Jackson and printed the exact format but the array changes in the response.
public Jedi sendRequest(String url, JsonNode request) {
return webClient.post()
.uri(url)
//.bodyValue(request)
.body(BodyInserters.fromValue(request))
.retrieve()
.bodyToMono(Jedi.class)
.blockOptional()
.orElseThrow();
}
}
A similar format change happens to "filters" as well. For example, filters.male=1
I tried both body and bodyValue functions but the result is the same. I am missing something but could not figure out what it is. Any help would be great.
Thanks!
You need to send it as a String that holds valid JSON format. SpringBoot uses JSON Jackson lib and will parse it. It is a responsibility of your SpringBoot end point to parse it. So, really you should ask person who developed the endpoint what info is expected. But in most cases it is String that holds valid json. It might be required that json conforms to particular schema but that is the info that should be included in your endpoint requirments
Thanks to Michael for stating the point of using String in the request. I converted the whole JSON into a String but what I should have done was convert the "filters" and "address" into a String as well.
private ObjectNode setRequestWithMapper() throws JsonProcessingException {
ObjectNode request = objectMapper.createObjectNode();
//other key values ..
String AddressArrayStr = objectMapper.writeValueAsString(addressArray);
String filterStr = objectMapper.writeValueAsString(filters);
request.put("address", AddressArrayStr );
request.put("filters", filtersStr);
return request;
}
And for the body;
public Jedi sendRequest(String url, JsonNode request) {
return webClient.post()
.uri(url)
.bodyValue(request)
.retrieve()
.bodyToMono(Jedi.class)
.blockOptional()
.orElseThrow();
}
}

Spring-webflux filter to fetch the request body

I need to fetch the entire request body in filter and convert it into String. Below is my code but nothing is getting printed on console.
#Component
public class WebFilter01 implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange,
WebFilterChain webFilterChain) {
Flux<DataBuffer> requestBody = serverWebExchange.getRequest().getBody();
Flux<String> decodedRequest = requestBody.map(databuffer -> {
return decodeDataBuffer(databuffer);
});
decodedRequest.doOnNext(s -> System.out.print(s));
return webFilterChain.filter(serverWebExchange);
}
protected String decodeDataBuffer(DataBuffer dataBuffer) {
Charset charset = StandardCharsets.UTF_8;
CharBuffer charBuffer = charset.decode(dataBuffer.asByteBuffer());
DataBufferUtils.release(dataBuffer);
String value = charBuffer.toString();
return value;
}
}
Nothing is getting printed on console because you did not subscribe to decodedRequest ,
as we know one of the Reactive aspect:
Nothing happens until you subscribe
But if you do that you will see printed body on console but your code will not work, because the next operators cannot read the body and you will get IllegalStateException(Only one connection receive subscriber allowed.)
So, how to resolve it?
Create your own wrapper for ServerWebExchange (please read about this here: How to log request and response bodies in Spring WebFlux)
Log bodies in HttpMessageDecoder. If you see, for instance, AbstractJackson2Decoder you will found code where Spring decode you buffer to object and can log it:
try {
Object value = reader.readValue(tokenBuffer.asParser(getObjectMapper()));
if (!Hints.isLoggingSuppressed(hints)) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(value, !traceOn);
return Hints.getLogPrefix(hints) + "Decoded [" + formatted + "]";
});
}
return value;
}

How test Post request with custom object in content type application/x-www-form-urlencoded?

I have controller:
#PostMapping(value = "/value/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateSettings(final Dto dto) {
System.out.println(">>> " + dto);
return "template";
}
Controller works if I send request across chrome window. But when I write test for this method I get problem. Not converted object, value not inserted.
Test:
#Test
#WithMockUser(username = FAKE_VALID_USER, password = FAKE_VALID_PASSWORD)
public void test_B_CreateDtoWithValidForm() throws Exception {
final Dto dto = new Dto();
dto.setId("value");
dto.setEnabled("true");
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content(dto.toString()))
.andDo(print());
}
Output is >>> Dto{id=null, enabled=false}
How test Post request with custom object in content type application/x-www-form-urlencoded?
In this case you don't need to use content, but instead you need to use param in this way:
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "value")
.param("enabled", "true"))
.andDo(print());

Download A File On click of a link using spring mvc

When I click on any link the content should be downloaded
But this is what I get.
MastercourseController.java
#RequestMapping(value = { ControllerUriConstant.download_file }, method = RequestMethod.GET)
#ResponseBody
public void downloadingAFileById(#RequestParam("id") String id, Model model, HttpServletRequest request)
throws TechnoShineException, IOException {
String filePath = "D:/dev/testFIle.txt";
long download = Long.parseLong(id);
byte[] b = masterCourseFileFormService.getAllDownloadable(download);
OutputStream outputStream = new FileOutputStream(filePath);
outputStream.write(b);
outputStream.close();
}
MasterCourseService
public byte[] getAllDownloadable(long id) throws TechnoShineException
{
return masterCourseFormUploadDao.getAllDownloadableFiles(id);
}
MasterCourseDao
public byte[] getAllDownloadableFiles(long id) throws TechnoShineException
{
return masterCourseFormUploadMapper.getAllDownloadable(id);
}
MasterCourseMapper
public byte[] getAllDownloadable(long id) throws TechnoShineException;
You are writing the data returned by getAllDownloadable(..) to a hard-coded file. Are you sure that is what you want? I think you want to write the content returned by getAllDownloadable(..) to be written into the response. That can be done by adding a method parameter of the type HttpServletResponse to your mapping and writing into the output stream returned by HttpServletResponse#getOutputStream() and flushing (not closing!) that stream at the end.
Furthermore you have to remove the #ResponseBody annotation as this is meant to be used if the value that is returned by the mapping method returns the data that should directly be sent to the client (i.e. when sending a JSON data object or a string) without passing it to the template engine. As you are not returning anything you can remove this annotation.
Furthermore you have to set the content type of your response by invoking HttpServletResponse#setContentType(contentType: String).
In your case, the invocation would be the following:
response.setContentType("text/plain");
You complete method would look like this:
#RequestMapping(
value = ControllerUriConstant.download_file,
method = RequestMethod.GET
)
public void downloadingAFileById(#RequestParam("id") String id, HttpServletResponse response)
throws TechnoShineException, IOException {
long download = Long.parseLong(id);
byte[] b = masterCourseFileFormService.getAllDownloadable(download);
response.getOutputStream().write(b);
response.getOutputStream().flush();
}

Spring RestRemplate postforobject with request parameter having integer value

I have a method in Spring rest service.
#RequestMapping(value = "test/process", method = RequestMethod.POST)
public #ResponseBody MyResponse processRequest(String RequestId, int count)
I am using Spring RestTemplate to call this service like this.
RestTemplate restTemplate = this.getRestTemplate();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("RequestId", RequestId);
map.add("count", count);
restTemplate.postForObject(url, map,MyResponse.class);
When I try to invoke the client method I get the exception that no suitable HttpMessageConverter found for request type [java.lang.Integer]
org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [java.lang.Integer]
at org.springframework.http.converter.FormHttpMessageConverter.writePart(FormHttpMessageConverter.java:310)
at org.springframework.http.converter.FormHttpMessageConverter.writeParts(FormHttpMessageConverter.java:270)
at org.springframework.http.converter.FormHttpMessageConverter.writeMultipart(FormHttpMessageConverter.java:260)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:200)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:1)
at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:596)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:444)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:287)
I know one of the ways is to pass all the parameters as String. But I might need to pass complex data types as parameters later.
What is the ways to achieve this.
I have googled and some option seem to be writing my own converters. How should I start about solving this problem.
The root cause of this error is that by specifying an Integer in the LinkedMultiValueMap, the RestTemplate will take that to mean that your request is a multipart request. There is no HttpMessageConverter registered by default that can handle writing values of type Integer to a request body.
As you said, you can handle this situation by changing the count to be a String. After all, there is no Integer type in HTTP request parameters. However, you were worried
But I might need to pass complex data types as parameters later.
Assume something like this
public #ResponseBody MyResponse processRequest(String RequestId, int count, Complex complex) {
with
public class Complex {
private String someValue;
private int intValue;
public String getSomeValue() {
return someValue;
}
public void setSomeValue(String someValue) {
this.someValue = someValue;
}
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public String toString() {
return someValue + " " + intValue;
}
}
The the following will work just fine
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("RequestId", "asd");
map.add("count", "42");
map.add("someValue", "complex");
map.add("intValue", "69");
restTemplate.postForObject(url, map,MyResponse.class);
Remember that the request parameters are used to populate the fields of model attributes by their names.
An even better solution would have you using a serialization standard like JSON or XML.

Resources