Parameter in REST method is not properly handled in Retrofit - java

I'm working on Android app which connects to REST and invokes a method. This is Embarcadero REST DataSnap.
Using parameters like "#Query" is good when you invoke method like that:
www.app.net/api/searchtypes/862189/filters?Type=6&SearchText=School
However, here methods are invoked differently:
/datasnap/rest/some_class/some_method/some_parameter
Below is simple class to handle parameter which goes in request body:
public class Dane {
private int NAGL;
public Dane(int NAGL) {
this.NAGL = NAGL;
}
}
When I try to use Retrofit annotation #Query, for example:
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/{aSelectSQL}")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json,#Query("aSelectSQL") String aSelectSQL);
String dane = Credentials.basic("admin","admin");
Dane json = new Dane(11101);
Call<Logowanie> sql = gerritAPI.selectSQL(dane,json,"select n.datadok from nagl n where n.id_nagl =:NAGL");
and I launch the app, I see in logs
TstBaseMethods.SelectSQL: {aSelectSQL} << {"NAGL":11101}
The content of aSelectSQL is not sent to the server. I've already noticed that if I hardcode the content into URL and I invoke this as below, it works:
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/select%20n.datadok%20from%20nagl%20n%20where%20n.id_nagl%3D%3Anagl")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json);
Is there any way to pass properly content of the parameter to the server? It won't be good to hardcode all parameters in URL.

So, in retrofit, the #Query annotation is used for query parameter.
It will add your parameter as a query parameter, for example:
#GET("/api/somePath")
Call<JSONObject> getSomething(#Query("foo") String foo);
...
service.getSomething("bar")
Will actually result in the url:
https://yoursite.com/api/somePath?foo=bar
Here, in your case, you are using {} inside the url, which indicates retrofit that you are adding a path parameter. So your post should be like this
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/{aSelectSQL}")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json,#Path("aSelectSQL") String aSelectSQL);

Related

RestTemplate GET exchange with Set<Long> parameter

I have two services (A and B). I want to send a GET request from service A to service B. Here how my request looks like (Service A):
public Set<StudentDTO> getStudentsByIds(Set<Long> ids) { //here may be a set of ids [123213L, 435564L]
return restTemplate.exchange("localhost:8090/students/all?ids={ids}",
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<StudentDTO>>() {}, ids).getBody();
}
Here how my Service B controller looks like:
#RequestMapping("/students")
public class StudentController {
#GetMapping("/all")
public Set<StudentDTO> getStudentsByIds(#RequestParam Set<Long> ids) {
return studentService.getStudentsByIds(ids);
}
}
I am having trouble with sending set as parameter. I guess we can't put Set as parameter. I tried already turning set to String and removing the brackets from it like following and it worked:
String ids = ids.toString().substring(1, ids.toString().length() - 1);
But maybe there better solution or is there any solution to send Set?
My url looks like this: localhost:8090/students/all?ids=id1&ids=id2&ids=id3
Your url is being formed incorrectly. By using all?ids={ids} the resulting url sent to the service layer is http://localhost:8090/students/all?ids=%5B23677,%2012345,%201645543%5D. This is because the brackets from the set are being added to the url, but aren't interpreted properly. You can fix this by sending it as a comma delimited String appended to the URL, like this.
public Set<Long> getStudentsByIds(Set<Long> ids){
String studentIdsUrl = "http://localhost:8080/api/all?ids=" + ids.stream().map(Object::toString).collect(Collectors.joining(","));
return restTemplate.exchange(studentIdsUrl,
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<Long>>() {}, ids).getBody();
}

How i can pass a list<String> to the server with url

I am trying to pass the List of String from one server to the another server in spring boot.
How i can get that list at the another server?
The code i have tried-
public void addNewMostPopular(List<String> totalList){
try {
HttpHeaders httpHeaders = getHttpHeaders();
HttpEntity<String> httpEntity = new HttpEntity<String>(null, httpHeaders);
ResponseEntity responseEntity = restTemplate.exchange(
BASE_URL + "addMostPopular/"+new ArrayList<>(totalList), HttpMethod.POST, httpEntity,TrendingCategoryDTO.class);
}
and at server side i tried to get like-
#RequestMapping(value="/addMostPopular/[{totalList}]", method = RequestMethod.POST)
public void addMostPopularProduct( #PathVariable List<String> totalList) {}
Past long object in the url is a bad praxis, thats because spring url interpreter has a maximun lenght, so if you pass more than 2048 or 4096 char in some cases your request will return Response 400 bad request and won't execute anycode on your spring server.
After this aclaration, is there any option to pass a list? Yes, of course! But we need use #RequestBodylike this:
#PostMapping("/addMostPopular")
public void addMostPopularProduct(#RequestBody List<String> totalList) {
// Your function
}
Now we need to add to our other server the List we want to pass to this request in the body of the request.
If you like to pass a List of values in the url one possibility is to pass them as url parameters.
You have to create a link similar to the followings:
http://youserver/youraction?param=first&param=second&param=third
or
http://youserver/youraction?param=first,second,third
Your controller in spring must be something like
#Controller
public class MyController {
#GetMapping("/youraction")
public String yourAction(#RequestParam("param") List<String> params) {
// Here params is tre list with the values first, second, third
}
}
This action is able to handle both kind of requests that I wrote before.
There are many ways to pass infomation between servers.
The simple way is to initiate an http request, based on your request method get or post put the parameters to the appropriate location : reuqest header or request body. You can do like #Davide Lorenzo MARINO.
Or use a message queue, like ActiveMq.
In the case of the same registry center, you can also use #feign to resolve it.

How to send Integer value in the form of JSON format and recieve in REST Controller?

I have one REST Controller where I have written this code
#PostMapping(value = "/otp")
public void otp(#RequestBody Integer mobile) {
System.out.println(" Mobile = "+mobile);
}
And I am calling this method from Postman with the following inputs
URL : localhost:8080/otp
Body :
{
"mobile":123456
}
But I am getting the following exception
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.lang.Integer out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token
If I am taking String as a parameter like this
#PostMapping(value = "/otp")
public void otp(#RequestBody String mobile) {
System.out.println(" Mobile = "+mobile);
}
And passing the inputs as
{
"mobile":123456
}
Now it is printing in the console as follows
Mobile = {
"mobile":"123456"
}
But I want only this value 123456. How to achieve my requirement?
NOTE: I don't want to create any additional POJO class or even I don't want to send the data using query/path parameter.
If you want to send your body like:
{
"mobile":123456
}
You will create another object to receive the value.
But if you only want to accept the integer value without any other object, you will not put json object in request body, but only the integer itself.
Body:
12345
You can use a class as request body:
class Request {
public Integer mobile;
}
and specify the parameter like this:
public void otp(#RequestBody Request mobile) {
...
Create a pojo class like below.
public class Mobile{
private Integer mobile;
//getter and setter
}
And then
public void otp(#RequestBody Mobile mobile)
to print value use
mobile.getMobile();
Converting process with json and #RequestBody is automatically and need you provide a class which contains proper field.If you insist to send data by request body,you could use String to receive json data as String.For example:
public void test(#RequestBody String request){
log.info(request);
}
In this way the request body you received is a String.You need some other tool to help you convert it.Like org.json,you could get more info from here HttpServletRequest get JSON POST data
But the easiest way is creating a new class to receive the data or changing #RequestBody to #RequestParam or #Pathvariable.
If you still want to use json as the request body,maybe you could create a common class A which contain lots of fields like name,phone number,email...Then after you send a request which only contains mobile,you just need to A.getMobile().In this way, even you get 100 request,you still need one POJO(but not recommend)
Just send the number in JSON.
12345
use #RequestBody to receive the int number.
it will work.
I use postman to send it in RAW JSON.
BTW, I am a beginner learning Spring Boot.
if you have org.json.JSONObject
#PostMapping(value = "/otp")
public void otp(#RequestBody String mobile) {
JSONObject obj = new JSONObejct(mobile);
System.out.print(obj.getInt("mobile"));
}

Pass parameters in the client

I use a RESTful Web Service. In this web service I must pass a bean that I want to save as a parameter.
Here is the server code:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Unidade inserir(Unidade unidade){
Session s = ConnectDb.getSession();
try {
s.getTransaction().begin();
s.save(unidade);
s.getTransaction().commit();
return unidade;
} catch (Exception e) {
e.printStackTrace();
s.getTransaction().rollback();
return null;
} finally {
s.close();
}
}
I have the following code in the client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Builder builder = webResource.accept(MediaType.APPLICATION_JSON);
GenericType<Unidade> genericType = new GenericType<Unidade>() {};
Unidade u = new Unidade();
u.setUnidSigla("KG");
//How to pass this bean as parameter?
Unidade response = builder.post(genericType);
System.out.println(response);
How can I pass the bean to the method as a parameter?
Using Jackson as a Serializer/DeSerializer
If your Unidade object is annotated with Jackson and/or a Deserializer is registered then you should be able to POST with a BODY that contains the JSON representing the Unidade object. It should be magically deserialized and rebuilt as an object on the server side.
Important
Make sure that you add a Content-Type header in the POST request with a value of application/json. Without this header your Jersey might not know what to do with the body.
You would use the Jackson ObjectMapper to serialize your Unidade object to JSON and send that instead of whatever that GenericType stuff is.
I have both Jersey and RESTEasy implementations that work seamlessly with Jackson in this manner.
How can I pass the bean to the method as a parameter?
Checkout the documentation for the post method:
/**
* Invoke the POST method with a request entity that returns a response.
*
* #param <T> the type of the response.
* #param c the type of the returned response.
* #param requestEntity the request entity.
* #return an instance of type <code>c</code>.
* #throws UniformInterfaceException if the status of the HTTP response is
* greater than or equal to 300 and <code>c</code> is not the type
* {#link ClientResponse}.
* #throws ClientHandlerException if the client handler fails to process
* the request or response.
*/
<T> T post(Class<T> c, Object requestEntity)
throws UniformInterfaceException, ClientHandlerException;
The method takes two parameters. First parameter is the expected response type, and second one is the entity which is going to be put in the request body.
What happens here, when performing the request Jersey would serialize the object passed as a request entity into the JSON string (hence you've set the header - accept(MediaType.APPLICATION_JSON)). When the response from the server arrives, Jersey will deserialize it (the inverted process as in case of requestEntity) and return you the object.
And what if my method receives more than 1 parameter? Because the post
method only acepts 1
Well you cannot do it with JAX-RS, and it makes little sense actually. You can pass multiple parameters to the method as #PathParam or a #MatrixParam, but there can be only one associated with the body (well you have only one body in our request, right?). Checkout answer to this question and checkout how to use #PathParam or #MatrixParam
Let's suppose instead of returning a "Unidade" class, my method
returns a String. So, it will receive a "Unidade" as parameter and
return a "String". How can i retrieve it in this case, passing the
"Unidade" instance as before?
I believe you could achieve that with post(String.class, unidadeInstance). The first parameter doesn't have to be the same as the second. It's valid to accept one parameter and return another. It is even valid to take a parameter and return nothing in the body (like you have done it in the code attached to your question). You could accept the body and send back response containing status 201 Created and Location header entry pointing to the URL of the newly created resource.
Not sure what's your purpose for GenericType. Anyway, try the code below.
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
Unidade u = new Unidade();
u.setUnidSigla("KG");
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Unidade response = webResource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.post(Unidade.class, u);
I'm not sure if it helps but i had a similar problem.
My scenario was i need a webservice which had to receiver a bunch of values which are organized as a kind of profile. But this service has to handle that there are comming more profiles where still old clients using the service. The interface had to be as static as possible.
Our solution is very simple. We only post a single text field as content of the post. But this includes the serialized state of an profile object in JSON.
Pseudo code:
public class Profile1 {
...
public String asJSON() {
JSONObject obj = new JSONObject();
obj.put("profileAtr1", profileAtr1);
...
return obj.toString()
}
}
formParams.put("profile", profile.asJSON());
client.post(formParams);
This way it's not automaticly deserialized but it's easy to do it by hand.
We do this with a generic Profile object which can be created out of JSON String in the constructor.
Pseudo code:
public GenericProfile {
public GenericProfile(String json) {
JSONObject obj = new JSONObject(json);
String profileName = obj.getString("profileName");
if (profileName.equals("Profile1") {
this = new Profile1(obj); // I know this is not working ;) My impl. is a litle bit more complecated as that. I think i use a static method in the generic profile to create an instance i need.
} ...
}
}
And then in your webservice only have this one form param to handle and to deserialize ;)
Pseudo code:
public ResponseEnvelope coolServiceFunction(#FormParam("profile") String profileData) {
GenericProfile profile = new GenericProfile(profileData);
if (profile instanceof Profile1) {
do what you want
}
}
Sorry for the pseudo code, but i had alleady shutdown my dev vm and have no access to any repository any more :(
I think the biggest benefits from this solution is:
1. It can transport anything you can pack in JSON. I transfer BASE64 encoded binary blocks and heavyly encrypted textdata this way.
2. The easiest tutorial example of your REST Framework of the POST Service will provide all you need to do this.
3. You can be sure that your interface will stay for a long period of time.
Hope that helps

Attaching parameters in calling Apis

I have writing Java code Using Jersey library to call Rest APIs.
For my first method to display all blogs i have written the code like
return webResource.path(ConfigurationUtil.LIST_BLOGS).header(ConfigurationUtil.AUTHENTICATION_HEADER, authentication)
.accept(MediaType.APPLICATION_XML_TYPE).get(new GenericType<List<CommunityBean>>() {
});
which lists all the blogs.. As my LIST_BLOGS string is like
public static final String LIST_BLOGS = "api/blogs.xml";
Its works fine..
Now I'm trying to write a code for a method where I want to extract only 2 blogs and not all
so my url will be like
public static final String LIST_BLOGS = "api/blogs.xml?limit=2";
As I am not able to send the parameter from the wrapper file to ConfigurationUtil file and I used the way as
public List<BlogBean> searchBlogsXml(String limit) {
final String SEARCH_BLOGS="api/blogs.xml?limit="+limit;
return webResource.path(SEARCH_BLOGS).header(ConfigurationUtil.AUTHENTICATION_HEADER, authentication)
.accept(MediaType.APPLICATION_XML_TYPE).get(new GenericType<List<BlogBean>>() {
});
}
When i used like above i am getting 406 error..
Why so how to avoid this ?
Please give suggestions..
You can attach a query param like this;
resource.queryParam("limit", 2).get(MyObject.class);

Categories