How to encode url string with object in request parameters spring [duplicate] - java

This question already has answers here:
Using RestTemplate in Spring. Exception- Not enough variables available to expand
(6 answers)
Closed 3 years ago.
I.m trying to do request with the object as a request parameter in my Spring application.
new RestTemplate().getForObject("http://127.0.0.1:8080/items?filter={\"level\":\"BIG\"}", Item.class);
But get error:
As I discovered, the problem occurs when UriBuilder parses the string with url. I.e. new URI(...) gives the error. When I do the same request with Postman I get a desirable response. How I can properly encode url? I`m tried to use java.net.URLEncoder but this gives "IllegalArgumentException: URI is not absolute" error.
UPDATE:
Filter class in the request is used to do parameterized query ( Query() ) in mongodb in the remote server. Filter class:
#Getter
#NoArgsConstructor
public class Filter {
private Map<String, String> criteria;
#JsonAnySetter
public void add(String key, String value) {
if (criteria == null) {
criteria = new HashMap<>();
}
criteria.put(key, value);
}
}
Item object class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Item {
private String id;
private String name;
private Instant timestamp;
}

i have problem like yours and i solve it using following snippet code
UriComponentsBuilder renewURIBuilder= UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8080").path("/items").queryParam("filter", "{'level':BIG}");
UriComponents uriComponent=renewURIBuilder.build(true);
URI uri=uriComponent.toUri();
then call your resttemplate like the following
new RestTemplate().getForObject(uri, Item.class);
hope it help you

Related

Java instanceof with Object

I have a spring boot project with Java 17. I am calling a third party API to get the list of records. If the records are present I do get the 200 OK response with list of records and in case records are not present I do get a 200 OK response with another JSON schema. To verify whether the response if of a type list of records or an error I am using instanceof but it is not working and always go to else condition.
HttpEntity<Void> request = new HttpEntity<>(httpHeaders);
ResponseEntity<Object> response = restTemplate.exchange(uri, HttpMethod.GET, request, Object.class);
if (response.getBody() instanceof ZohoError zohoError) {
return Collections.emptyList();
} else {
return (List<Leave>) response.getBody();
}
#Getter
#Setter
#ToString
#EqualsAndHashCode
public class ZohoError {
private String message;
#JsonAlias("errorcode")
private String errorCode;
#JsonAlias("Response status")
private int responseStatus;
}
As your third party API returns different schemas with same http code (which smells for me), you can't use RestTemplate to get needed object - you don't know the object type.
You can get RestTemplate response as String, parse it to JsonNode using ObjectMapper (Jackson). Then decide which schema is this by some telling attributes. And parse response string to defined type using ObjectMapper.

Problem with getting data from external source by Rest Template

I have some problem with getting response from external resources with RestTemplate.
What I want to get is data from PokemonApi: https://pokeapi.co/api/v2/type/
I thought about creating 2 Classes that will contain that data(Not using Object and headers)
My 2 classes are like:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MainResponse {
#JsonProperty("typeList")
private List<PokemonType> pokemonTypes;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class PokemonType {
#JsonProperty("name")
private String pokemonType;
}
What problem I got is that I can't extract any data from my RestTemplate class:
public MainResponse response() {
Optional<MainResponse> search = Optional.ofNullable(
restTemplate.getForObject("https://pokeapi.co/api/v2/type", MainResponse.class)
);
return search.orElseGet(MainResponse::new);
}
I'm not sure why its not working right. I can't get any response and everything is ending in errors when I try get response. Please someone look at that and give me some pointers.
It will be surely eyeopener for all RestTemplates in the future for me :) Thanks alot.

Best practice to deserialize a JSON using Spring MVC

I'm developing a web application using Spring MVC; it works as follows:
a JSON request is created from a model object (a POJO)
it's sent to an external server that exposes some APIs
a JSON response is received by the controller
I need to figure out which is the best way to serialize and deserialize JSON in a Java web application. At first I was using Gson but then I began to wonder if it was the right choice.
Is it possible to serialize and deserialize JSON to/from model
object using #RestController annotation? How does it works? I only find examples of how to create a rest service, in this sense
What is the best practice in this case? RestController? Gson? Jackson? Other?
N.B. My JsonResponse object has a generic field because the responses are all the same except that for a value. An example:
AccountRequest.class
public class AccountRequest() {
private String jsonrpc;
private String method;
private Map<String, Object> params;
private int id;
//constructor, getters and setters
}
JsonResponse.class
public class JsonResponse<T> {
private String jsonrpc;
private T result;
private String id;
//constructor, getters and setters
}
LoginResult.class
public class LoginResult {
private String token;
private String product;
private String error;
private Date lastLoginDate;
//constructor, getters and setters
}
MyController.class
#Controller
public class LoginController {
String response;
[...]
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView doLogin(#ModelAttribute("user") User user) {
[...]
//???
[...]
}
}
I open connection, send request and save the response in response String in MyController class. User parameter is annotated with #ModelAttribute to retrive the User object created by the showLoginForm() method.
Using Gson I know how to serialize request from my model object:
AccountRequest accountRequest = new AccountRequest();
accountRequest.setJsonrpc("2.0");
accountRequest.setMethod("method");
accountRequest.setParams(params);
accountRequest.setId(1);
String gson = new Gson().toJson(accountRequest);
JSONObject accountRequestJson = new JSONObject(gson);
And how to deserialize response in my model object:
Type jsonType = new TypeToken<JsonResponse<LoginResult>>() {}.getType();
JsonResponse<LoginResult> jsonResponse = new Gson().fromJson(response, jsonType);
The response is now saved into jsonResponse object, and I can put it in my ModelAndView.
Now, how can I deserialize it in my model object using #RestController and Jackson? Can you confirm it's the better method to do that? Can you post an example?
I hope it's not a silly question.
Thanks in advance!

How to parse JSON Response to POJO with AndroidAnnotations?

I'm using AndroidAnnotations to build a Rest for an Android Application.
On the Serverside im using PHP, which send a json looking like :
{"tag":"register","success":0,"error":2,"msg":"User already existed","body":[]}
I have two POJOS :
User.java:
public class User implements Serializable {
private String name;
private String email;
private String password;
//getter and setter Methods
}
Response.java:
public class RegistrationResponse implements Serializable {
private String tag;
private int success;
private int error;
private String msg;
private String body;
//getter and setter Methods
}
Rest Client:
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
Response sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
Activity.java:
//User and Response are POJOs
Response result = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
But i got an Null Pointer Exception error on Activity.java. But if i change the return value of "sendUserRegistration" function to String all work. So my "Response" POJO seems not to be converted from AndroidAnnotations.
How can i convert the Rest Response to my "Response"-POJO using AndroidAnnotations?
You don't need to return the entire response object per rest call, just set the response to your custom object. Or you can also return a JsonObject also and use gson to convert it later on.
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
User sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
then just simply call
User newUser = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
AA relies on Spring Android RestTemplate to make the rest call. And in order to build requests and handle responses this lib uses converters. And to know which converter the RestTemplate should use, it checks the content-type response header.
As MappingJacksonHttpMessageConverter and GsonHttpMessageConverter handles only http response with content-type=application/json and your result is converted to string, I'm pretty sure you forgot to set this header in your php server. So it send the default one (ie: text/plain) which is only handle by StringHttpMessageConverter.
Also, the body field is an object in your json example, but in your POJO you declared it as a String. So parsing will fail on this point.

How to pass List or String array to getForObject with Spring RestTemplate

I am developing some restful services with Spring. I have trouble with passing/getting string array or large string as parameters to my service controller. My code examples are like below;
Controller:
#RequestMapping(value="/getLocationInformations/{pointList}", method=RequestMethod.GET)
#ResponseBody
public LocationInfoObject getLocationInformations(#PathVariable("pointList") String pointList)
{
// code block
}
Sample point list:
String pointList = "37.0433;35.2663,37.0431;35.2663,37.0429;35.2664,37.0428;35.2664,37.0426;35.2665,37.0424;35.2667,37.0422;35.2669,37.042;35.2671,37.0419;35.2673,37.0417;35.2674,37.0415;35.2674,37.0412;35.2672,37.0408;35.267,37.04;35.2667,37.0396;35.2665,37.0391;35.2663,37.0388;35.2662,37.0384;35.266,37.0381;35.2659,37.0379;35.2658,37.0377;35.2657,37.0404;35.2668,37.0377;35.2656,37.0378;35.2652,37.0378;35.2652,37.0381;35.2646,37.0382;35.264,37.0381;35.2635,37.038;35.263,37.0379;35.2627,37.0378;35.2626,37.0376;35.2626,37.0372;35.2627,37.0367;35.2628,37.0363;35.2628,37.036;35.2629,37.0357;35.2629,37.0356;35.2628,37.0356;35.2628,37.0355;35.2626";
Web service client code:
Map<String, String> vars = new HashMap<String, String>();
vars.put("pointList", pointList);
String apiUrl = "http://api.website.com/service/getLocationInformations/{pointList}";
RestTemplate restTemplate = new RestTemplate();
LocationInfoObject result = restTemplate.getForObject(apiUrl, LocationInfoObject.class, vars);
When I run client side application, it throws a HttpClientErrorException: 400 Bad Request, I think long location information string causes to this problem. So, how can I solve this issue? Or is it possible posting long string value as parameter to web service?
Thx all
List or other type of objects can post with RestTemplate's postForObject method. My solution is like below:
controller:
#RequestMapping(value="/getLocationInformations", method=RequestMethod.POST)
#ResponseBody
public LocationInfoObject getLocationInformations(#RequestBody RequestObject requestObject)
{
// code block
}
Create a request object for posting to service:
public class RequestObject implements Serializable
{
public List<Point> pointList = null;
}
public class Point
{
public Float latitude = null;
public Float longitude = null;
}
Create a response object to get values from service:
public class ResponseObject implements Serializable
{
public Boolean success = false;
public Integer statusCode = null;
public String status = null;
public LocationInfoObject locationInfo = null;
}
Post point list with request object and get response object from service:
String apiUrl = "http://api.website.com/service/getLocationInformations";
RequestObject requestObject = new RequestObject();
// create pointList and add to requestObject
requestObject.setPointList(pointList);
RestTemplate restTemplate = new RestTemplate();
ResponseObject response = restTemplate.postForObject(apiUrl, requestObject, ResponseObject.class);
// response.getSuccess(), response.getStatusCode(), response.getStatus(), response.getLocationInfo() can be used
The question is related to GET resource, not POST. Because of that I think that "accepted answer" is not the correct one.
So for other googlers like me that finds this, Ill add what helps me:
GET resources can receive a string list via #PathVariable or #RequestParam and even correctly bind it to a List<String> if you do pass the list separated by ,.
Your API can be:
#RequestMapping(value="/getLocationInformations/{pointList}", method=RequestMethod.GET)
#ResponseBody
public LocationInfoObject getLocationInformations(#PathVariable("pointList") List<String> pointList) {
// code block
}
And your call would be:
List<String> listOfPoints = ...;
String points = String.join(",", listOfPoints);
String apiUrl = "http://api.website.com/service/getLocationInformations/{pointList}";
LocationInfoObject result = restTemplate.getForObject(apiUrl, LocationInfoObject.class, points);
Note that you must send lists to API using , as separator, otherwise the API cannot recognize it as a list. Also you cannot just add your list directly as a parameter, because depending on how it's mashalled the generated string may not be compatible.
Firstly you've passed a map as parameters but your controller expects these as a path variable. All you need to do is make the 'pointlist' value part of the URL (without the curly bracket placeholders). e.g.:-
http://api.website.com/service/getLocationInformations/pointList
Next you need to ensure you have message converters set up so that your LocationInfoObject is marshalled into an appropriate representation (suggest JSON) and unmarshalled the same way.
For the Rest template:
restTemplate.setMessageConverters(...Google MappingJackson2HttpMessageConverter...);
For the server you just need to add Jackson to the classpath (if you want multiple representations you'd need to configure each one manually - Google will be your friend here aswell.

Categories