Spring RequestBody convert JSON to String - java

I have a RestController class which has a method to search for a Film by its title:
#RequestMapping(value = "/film", method = RequestMethod.POST,
consumes = "application/json", produces = "application/json")
public Film getFilm(#RequestBody String filmSearch){
FilmInfo filmInfo = new FilmInfo();
Film film = filmInfo.getFilm(filmSearch);
return film;
}
If I send a json String
{
"filmSearch":"<title>"
}
to the endpoint from Postman I receive a blank response back.
I then did a
System.out.println(filmSearch)
right after entering the method to find the String filmSearch was exactly the JSON string I sent from Postman. My application is not seeing the JSON and extracting the value from filmSearch in my request to attach to the in-app String filmSearch.
If I remove the
consumes = "application/json"
part in the RequestMapping and send over a plain text string of the title it works and I get a Film object sent back as JSON.
I'd rather not use plain text in my search term though, how can I correctly have my JSON be converted into a String on entering the method?

If you add request body is String not Object. Server received is String json not OBJECT. You can try code:
#RequestMapping(value = "/film", method = RequestMethod.GET, produces = "application/json")
public Film getFilm(#RequestParam("search") String search){
FilmInfo filmInfo = new FilmInfo();
Film film = filmInfo.getFilm(search);
return film;
}
If you user postman:
URL: /flim?search=minion
Method: GET
Header: Content-Type: application/json

It's because you're passing the entire JSON payload as a string inside the 'getfilm()' function.
What you're expecting to call is getfilm(<title>), but you're actually calling is getfilm({"filmSearch":"<title>"} ), which is wrong.
The best choice would be, convert that string to JSON, say like this
JSONObject jsonstring = new JSONObject(filmSearch);
FilmInfo filmInfo = new FilmInfo();
Film film = filmInfo.getFilm(jsonstring.get("title"));
you can also ignore the 'consumes = "application/json"' from the request mapping.

Related

openapi-generator spring boot: combining json and text response

I am using the openapi-generator to create a spring-boot server. I am currently still in the experimentation phase.
The goal is that the server is able to provide multiple response media-types, depending on the media type that is accepted by the requester. According to different sources, this should be possible. See here for example of how the yaml file would then look. Other similar examples can be found here on stack overflow.
Concrete example. Let's say we have a post request where if a name is posted the name is returned (just a silly example). In case the requester sends the name John Doe and does not accept application/json, the response, in plain text, should look like this:
John Doe
In case the requester accepts application/json the response should look like this:
{"name": "John Doe"}
For explaining my question/problem I created an example spring boot server. At one point it has the path /user for which the response is:
responses:
'200':
description: The username.
content:
application/json:
schema:
properties:
name:
type: string
example: John Doe
text/plain:
schema:
type: string
example: John Doe
On the other hand I created the path /getuser (name is not really fortunate but it is for this example) which returns the following response:
'200':
description: The username.
content:
text/plain:
schema:
type: string
example: John Doe
application/json:
schema:
properties:
name:
type: string
example: John Doe
My problem is the following: for the first example, where I put the application/json first in the yaml file, the API looks like this:
default ResponseEntity<UserPost200Response> userPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
If, however, I would like to return a ResponseEntity<String> this gives an error since UserPost200Response is not used.
For the path /getuser, where the String response is first defined in the yaml file, my API looks like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "\"John Doe\"";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
This API makes it possible to return a ResponseEntity<String> but not a ResponseEntity<UserPost200Response> which defines the above mentioned json-model.
One workaround that I found, would be to use the path where the string-response is declared first in the yaml file (see /getuser) in the example above and that returns a ResponseEntity<String> and override and do something like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
if (mediaType.isCompatibleWith(MediaType.valueOf("text/plain"))) {
String exampleString = "John Doe";
ApiUtil.setExampleResponse(request, "text/plain", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
In this case, I don't use the Model created by the openapi-generator and treat the JSON basically as a string.
Another option is to go for a wildcard like ResponseEntity<?> but from what I understand, except proven the contrary, this seems bad practice. I haven't figured out how to declare this in the .yml file that is used by the openapi-generator.
Neither options seem to respect the contract.
I wonder if (1) I am doing something wrong here and (2) how it could be better implemented. The goal is of course to not rewrite the API's and only implement the logic in the Controllers. Any ResponseEntity in the API's should thus not be changed.

How to post list of objects in Spring?

I have a method in a controller
#PostMapping("/process")
suspend fun process(
#RequestParam("id") id: String,
#RequestParam("names") names: List<String>,
#RequestParam("repositoryUri") repositoryUri: String
) = coroutineScope {
...
}
I want to send post query from front-end desktop application and I try
val post = HttpPost(uri)
val builder: MultipartEntityBuilder = MultipartEntityBuilder.create()
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
builder.addTextBody("id", id, ContentType.DEFAULT_BINARY) // id is String, that is Ok
builder.addTextBody("names", names, ContentType.DEFAULT_BINARY) // names is List<String>, error
builder.addTextBody("repositoryUri", repositoryUri, ContentType.DEFAULT_BINARY) // Ok
val entity: HttpEntity = builder.build()
post.entity = entity
httpclient.execute(post)
But the second param in the controller method (names) is not a string.
The builder has only methods addTextBody and addBinaryBody (but it doesn't seem to fit)
How can I do it?
P.S. I use apache
You'll need a custom format for sending a stringlist as a http form param.
Convert the list e.g. to a comma separated string, you'll have to reconvert the string to a list on the server side.
Or us json:
ObjectMapper mapper = new ObjectMapper();
TextBody("names", mapper.writeValueAsString(names), ... )

How to annotate array of objects response in Swagger

I have to debug a REST API Java project that has been developed using Swagger.I'm new to it, so I'm a little bit confused on how to do certain things. For example, here's one method:
#GET
#Path("/location/name")
#Produces({MediaType.APPLICATION_JSON})
#Operation(
summary = "Get location information",
tags = {"Information"},
responses = {
#ApiResponse(responseCode = "200", content = #Content(schema = #Schema(implementation = LocationResponse.class)), description = "Get location information"),
#ApiResponse(responseCode = "500", description = "Error: Internal Server Error")
}
)
public Response searchLocationByName(
#Parameter(description = "Location name", required = true) #DefaultValue("Barcelona") #QueryParam("name") String locationName
) { /* METHOD CODE */ }
The #ApiResponse for the code 200 is not of type LocationResponse but of type ArrayList<LocationResponse>, as it can return more than one location. What would be the correct syntax for this change? I've been reading the documentation at https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#operation-annotations but I couldn't find an appropriate example...
Thanks!
Use #ArraySchema instead of plain #Schema to define input or output data for array types.
For the PageDto<T> we can simply create ResponseDto which extends PageDto<T> and use it in swagger as : #ApiResponse(responseCode = "200", content = #Content(array = #ArraySchema(schema = #Schema(implementation = ResponseDto.class))), description = "Get location information"),. Thank You

How to pass in a Java List in a GET call using Spring RestTemplate to use in Controller

I cannot figure out how to pass in a Java List into a Spring RestTemplate call and then use the list in the Controller method
Client code:
public String generateInfoOfIds(List<String> ids, String userName, String password){
RestTemplate restTemplate = new RestTemplate();
String response = null;
String url = "https://someservice.com/getInfoOfIds?";
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor(userName, password));
response = restTemplate.exchange(url+"ids=" + ids, HttpMethod.GET, null, String.class);
return response;
}
Controller code:
#RequestMapping(value = "/getInfoOfIds", method = RequestMethod.GET)
public String getInfoOfIds(#RequestParam("ids") List<String> ids) {
String response = myService.doSomeWork(ids);
return response;
}
The call to he controller itself works but he List is not converting to the #RequestParam ids correctly.
How exactly do you pass a List from a RestTemplate to a Controller in Spring?
response = restTemplate.exchange(url+"ids=" + ids, HttpMethod.GET, null, String.class);
In your client code it should be either comma separated values (ids=1,2,3) or multiple joined values (ids=1&ids=2&ids=3), but you're passing a whole object to restTemplate.exchange as a 1st argument which will be converted using toString() method and will look like this ids=[123, 321] and it differs from expected format.
Solution with formatted String
JDK >= 8
String idsAsParam = ids.stream()
.map(Object::toString)
.collect(Collectors.joining(","));
response = restTemplate.exchange(url+"ids=" + idsAsParam, HttpMethod.GET, null, String.class);
JDK < 8
String idsAsParam = ids.toString()
.replace(", ", ",") //remove space after the commas
.replace("[", "") //remove the left bracket
.replace("]", "") //remove the right bracket
.trim(); //remove trailing spaces from partially initialized arrays
response = restTemplate.exchange(url + "ids=" + idsAsParam, HttpMethod.GET, null, String.class);
Solution with RestTemplate
You can also do the trick by using UriTemplate's pattern matching feature. It works well with Map object as an argument. The key should match URI key and the value should be an array, which can be created from list. Example:
String url = "http://google.com";
List<String> params = Arrays.asList("111", "222", "333");
Map<String, Object> map = new HashMap<>();
map.put("ids[]", params.toArray());
restTemplate.exchange(url + "?ids={ids[]}", HttpMethod.GET, null, String.class, map);
Results into
"http://google.com?ids=111,222,333"

Parse querystrings of a request as JSON

I'm going to receive requests with a JSON object passed as querystring which will no longer have a JSON structure, it will be something like this:
http:/mysite/api/doSomething?name=name&address=address...
I need to store it as a String following a JSON structure, the problem is that the original JSON object passed as querystring may have attributes that are collections and I can't figure out a way to parse it back correctly.
Is there a Java function or a library to achieve this easily?
I assume you are using spring mvc/jersey in controller. In that case you can do something like this
#RequestMapping(value = "/mysite/api/doSomething", method = RequestMethod.GET)
public String search(
#RequestParam Map<String,String> allRequestParams) {
JSONObject js = new JSONObject();
for (Map.Entry<String,String> entry : allRequestParams.entrySet()){
js.put(entry.getKey(), entry.getValue());
}
String jsonString = js.toString();
}
Basically get all the queryparam and construct JSONObjcet, JACKSON library will not be of much use here.

Categories

Resources