RestTemplate GET exchange with Set<Long> parameter - java

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();
}

Related

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.

Parameter in REST method is not properly handled in Retrofit

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);

How to re-write request URI along with path variable in Spring?

I'm using Spring 4 right now and I have a controller defined a such:
#RequestMapping(value = "/{id}/definitions", method = RequestMethod.PUT)
#ResponseBody
public List<ResponseDTO> update(#PathVariable(value = "id") String id,
HttpServletRequest iRequest) throws Exception
{ ... }
I am expecting to receive an encrypted 'id' String coming as part of a path variable in the request itself. But what I need to do is re-write this request URI and decrypt it to another value (an integer, for example) and form another HTTP request with the original URI only with the transformed/decrypted value.
How can I get a hold of the entire URI and substitute the {id} with an integer?
For example, if the original request coming in looks like:
http://mycompany.com/my-service/kjAISOhalkjZjakmbbb/definitions
I want to transform everything after the context path:
/kjAISOhalkjZjakmbbb/definitions
to:
/123456/definitions
So finally, I can form a request to another service that might look like this:
http://mycompany.com/my-service-2/123456/definitions
Thank you!
Decrypt the value (however you do that).
Then just use a redirect or forward (probably a forward in your case):
#RequestMapping(value = "/{id}/definitions", method = RequestMethod.PUT)
public void update(#PathVariable(value = "id") String id,
HttpServletRequest iRequest) throws Exception {
String decryptedId = //decrypt here
//Do whatever else
//either forward: or redirect:
return "forward:my-service-2/" + decryptedId + "/definitions";
}

URL inside of another URL (as path)

I want to use the following type of URL in Restlet: http://url.com/http://www.anotherurl.com/path
As a result I want to get http://www.anotherurl.com/path as a parameter.
However it does nothing.
Also, if I use http://url.com/path , then I receive "path" without problems. http://url.com/www.anotherurl.com gives me www.anotherurl.com. However http://url.com/www.anotherurl.com/path is 404.
You need to encode the parameter special characters properly. Use URLEncoder to do so.
In fact, there are two parts here.
The URL building using the Reference class:
Reference ref = new Reference();
ref.setScheme("http");
ref.setHostDomain("localhost");
ref.setHostPort(8182);
ref.addSegment("test");
ref.addSegment("http://test");
ClientResource cr = new ClientResource(ref);
cr.get();
Getting the value as a path parameter and decode it. Here is the routing configuration in the application class:
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/test/{id}", MyServerResource.class);
return router;
}
And the corresponding code in the server resource:
public class MyServerResource extends ServerResource {
#Get
public Representation get() {
String id = getAttribute("id");
// Prints http%3A%2F%2Ftest
System.out.println("id = "+id);
// Prints http://test
System.out.println("id = "+Reference.decode(id));
return new EmptyRepresentation();
}
}
Hope it helps you,
Thierry

Consuming Spring Hateoas Restservice with RestTemplate

I have two applications, one is called bar, what provides me resources in HAL format. The other is bcm to consume that service.
Example of response bar looks like this:
[
{
"name":"Brenner/in",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/15"
}
}
},
{
"name":"Dienstleistungshelfer/in HW",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/4"
}
}
},
{
...
Now I try to consume that from bcm using Spring RestTemplate. My Solution works, but I am not happy with that solution somehow and I guess there is a more clean way.
My Client-Code consuming RestService looks like:
#Autowired private RestTemplate template;
#Override
#SuppressWarnings("unchecked")
public BerufListe findeAlleBerufe() {
final BerufListe berufListe = new BerufListe();
final ResponseEntity<List> entity = template.getForEntity(LinkUtils.findBeruf(), List.class);
if (OK.equals(entity.getStatusCode())) {
final List<LinkedHashMap> body = entity.getBody();
for (final LinkedHashMap map : body) {
final LinkedHashMap idMap = (LinkedHashMap) map.get("_links");
String id = remove(String.valueOf(idMap.get("self")), "href=");
id = remove(id, "{");
id = remove(id, "}");
final String name = String.valueOf(map.get("name"));
final Beruf beruf = new Beruf(id, name);
berufListe.add(beruf);
}
}
return berufListe;
}
There are few ugly code as you see. One of them is, that I don't have any generics for my collections. The other point, I get the Resource_ID very complicated, and I use StringUtils.remove many times to extract the self url.
I am sure there must be a more convenient way to consume HAL-Response by Spring.
Thanks you.
Take a look the the Resource class from spring-hateaos.
It provides methods to extract the links from the response.
However, as RestTemplate requires you to provide the class as variable, I have not found a different way other than creating a subclass of the desired entity and use it for RestTemplate.
You code could then look like this:
public class BerufResource extends Resource<Beruf> { }
BerufResource resource = template.getForEntity("http://example.at/berufe/1", BerufResource.class);
Beruf beruf = resource.getContent();
// do something with the entity
If you want to request a complete list, you would need to pass the array version of your entity to RestTemplate:
BerufResource[] resources = template.getForEntity("http://example.at/berufe", BerufResource[].class);
List<BerufResource> berufResources = Arrays.asList(resources);
for(BerufResource resource : berufResources) {
Beruf beruf = resource.getContent();
}
Unfortunately, we cannot write Resource<Beruf>.class which defeats the whole purpose of the generic class, as we need to again create a subclass for every entity. The reason behind that is called type erasure. I've read somewhere that they are planning to introduce generic support for RestTemplate but I am not aware of any details.
Addressing the extraction of the id from the url:
I would recommend to use a different model on the client side and replace the type of the id field with string and store the whole url in it. This way, you can easily refetch the whole entity whenever you like and do not need to construct the URL yourself. You will need the URL later anyway, if you plan on submitting POST-requests to your API, as spring-hateaos requires you to send the link instead of the id.
A typical POST-request could look like this:
{
"firstname": "Thomas",
"nachname": "Maier",
"profession": "http://example.at/professions/1"
}

Categories