I am trying to call a rest api and get the data from the api. I need to add dynamic parameters to the url in spring boot. I am a bit lost as how should I go about it. Can anyone kindly suggest me something?
RestTemplate restTemplate = new RestTemplate();
String consumeJSONString = restTemplate.getForObject("https://maps.googleapis.com/maps/api/geocode/json?latlng=5.47686,-73.961452&key=YOUR_API_KEY"
, String.class);
I would like to append latlng and api key in the url dynamically. I would really appreciate any suggestions.
You have to use the following variation of getForObject method
restTemplate.getForObject(url, responseType, uriVariables);
So it becomes..
String url = "https://maps.googleapis.com/maps/api/geocode/json?latlng={latlng}&key={key}";
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("latlng", "5.47686,-73.961452");
uriVariables.put("key", "YOUR_API_KEY");
String consumeJSONString = restTemplate.getForObject(url, String.class, uriVariables);
As i got a solution to dynamic variable
where this is a Rest Url
#RequestMapping(value = "/studentdetail")
public User detailStudent(#RequestParam(value = "userid", required = true) String userid) throws SQLException { /*your task goes here*/}
and this is what i m sending the dynamic params as userid which can be anything hopefully it will help u a lot
URL url = new URL("http://localhost:9090/testpapers?userid=" + userid);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
String s1=URLEncoder.encode(postDataParams, "UTF-8");
writer.write(s1);
writer.flush();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line = reader.readLine()) != null)
{
System.out.println(line);
}
writer.close();
reader.close();
}
If using Spring Framework, we have org.springframework.web.util.UriTemplate which does this in a structured way
Default URI in application.properties
we can define a parametrized uri in properties file. For eg
url.template.google.maps=https://maps.googleapis.com/maps/api/geocode/json?latlng={latitude},{longitude}&key={api_key}
Initialize UriTemplate
The template that we defined in properties file can now be read in a #PostConstruct block and stored in UriTemplate variable. I am saying #PostConstruct just to save trips to fetch the template for each request
#Value("${url.template.google.maps}")
private String googleMapsUri;
private UriTemplate googleMapsServiceUriTemplate;
#PostConstruct
private void init() throws URISyntaxException {
googleMapsServiceUriTemplate= new UriTemplate(googleMapsUri);
}
Expand variables and get URI
Now that we have everything, let's expand and get the uri
Given these values, call below method to get the URI
String latitudeValue = "5.47686";
String longitudeValue = "-73.961452";
String apiKeyValue = "api_key_value";
Below method would take the necessary values, create a map with these values and then expand the uri template to populate the values. We get a nicely structured URI this way.
pubic URI getGoogleMapsServiceUri(latitudeValue, longitudeValue, apiKeyValue) {
Map<String, String> templateVariables = new HashMap<>();
templateVariables.put("latitude", latitudeValue); //could use a variable here
templateVariables.put("longitude", longitudeValue); //could use a variable here
templateVariables.put("api_key", apiKeyValue); //could use a variable here
URI googleMapsServiceUri = googleMapsServiceUriTemplate.expand(templateVariables);
System.out.println("URL is " + googleMapsServiceUri.getPath());
return googleMapsServiceUri;
}
Related
Im new to Spring and im trying to understand it. Is it possible for me to use Spring boot to make calls to someone else's api? for example, the api im using needs to be authenticated using credentials they give me. I would like to use their API to make POST requests. So far I have found documents that relate to using your own API. These requests require headers to be passed as well.
Whats the simplest way I can do a POST request, passing my credentials and the required headers using Spring ?
Any help would be appreciated.
if i understand u well, u need to post another web-api with credentials okay you can use
restTemplate like the following example
public List<EtisAccount> getAllActiveAccount(){
logger.debug("Debug: in Class \t"+this.getClass().getName()+" Method Name is: \t"+new Object() {}.getClass().getEnclosingMethod().getName());
Properties sprinklrProp=sprinklrProperties.getSprinklrKeys();
SprinklrCredential sprinklrCredential=credentialBuilder.getSprinklrCredential();
RestTemplate restTemplate= new RestTemplate();
HttpHeaders header = new HttpHeaders();
header.setBearerAuth(sprinklrCredential.getAccess_token());
header.add("key", sprinklrCredential.getApi_key());
header.set("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
UriComponentsBuilder uriBuilder= UriComponentsBuilder.fromUriString(sprinklrProp.getProperty("sprinlrUri").toString())
.queryParam("types", sprinklrProp.getProperty("accountTypes").toString());
HttpEntity<String> entity= new HttpEntity<>(header);
sslCertificateValidation.disable();
ResponseEntity<String> sprinklrResponse=restTemplate.exchange(uriBuilder.toUriString(),HttpMethod.GET,entity, String.class);
List<EtisAccount> activeAccouts=etisAccountHelper.getAllSocialEtisAccounts(sprinklrResponse.getBody());
logger.debug(String.valueOf(sprinklrResponse.getStatusCodeValue()));
logger.debug(activeAccouts.toString());
return activeAccouts;
}
this is sample example for call online-API using RestTemplate
here i build header with credentials (bearer authentication)
HttpHeaders header = new HttpHeaders();
header.setBearerAuth(sprinklrCredential.getAccess_token());
header.add("key", sprinklrCredential.getApi_key());
header.set("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
and here i add header requestEntity to add to resttemplate request
HttpEntity<String> entity= new HttpEntity<>(header);
and here i get response using exchange method
ResponseEntity<String> sprinklrResponse=restTemplate.exchange(uriBuilder.toUriString(),HttpMethod.POST,entity, String.class);
You need following things :
Create a Spring web starter project from https://start.spring.io/
Create a new Java Class in your project and name it Controller. Add #RestController annotation to on class level.
Configure RestTemplate object in SpringBootApplication
Autowire that RestTemplate object in Controller class
Create a method in Controller class which using RestTemplate, will call the #PostMapping to their API.
You can try this way
This is the simple way to this
You can set user name and password as basic Auth
public class RESTInvoker {
private final String baseUrl;
private final String username;
private final String password;
public RESTInvoker(String baseUrl, String username, String password) {
this.baseUrl = baseUrl;
this.username = username;
this.password = password;
}
String getDataFromServer(String path) {
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(baseUrl + path);
URLConnection urlConnection = setUsernamePassword(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
return sb.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private URLConnection setUsernamePassword(URL url) throws IOException {
URLConnection urlConnection = url.openConnection();
String authString = username + ":" + password;
String authStringEnc = new String(Base64.encodeBase64(authString.getBytes()));
urlConnection.setRequestProperty("Authorization", "Basic " + authStringEnc);
return urlConnection;
}
}
I am trying to access the contents of an API and I need to send a URL using RestTemplate.
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url1, OutputPage .class);
But, I am getting the following error.
Exception in thread "main" java.lang.IllegalArgumentException: Not enough variable values available to expand '"price"'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:284)
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:220)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:317)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
at org.springframework.web.util.UriComponents.expand(UriComponents.java:162)
at org.springframework.web.util.UriTemplate.expand(UriTemplate.java:119)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:501)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239)
at hello.Application.main(Application.java:26)
If I remove the sort criteria, it is working properly.
I need to parse the JSON using sort criteria.
Any help will be much appreciated.
Thanks
The root cause is that RestTemplate considers curly braces {...} in the given URL as a placeholder for URI variables and tries to replace them based on their name. For example
{pageSize}
would try to get a URI variable called pageSize. These URI variables are specified with some of the other overloaded getForObject methods. You haven't provided any, but your URL expects one, so the method throws an exception.
One solution is to make a String object containing the value
String sort = "{\"price\":\"desc\"}";
and provide a real URI variable in your URL
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={sort}";
You would call your getForObject() like so
OutputPage page = restTemplate.getForObject(url1, OutputPage.class, sort);
I strongly suggest you do not send any JSON in a request parameter of a GET request but rather send it in the body of a POST request.
If the solution suggested by sotirios-delimanolis is a little difficult to implement in a scenario, and if the URI string containing curly braces and other characters is guaranteed to be correct, it might be simpler to pass the encoded URI string to a method of RestTemplate that hits the ReST server.
The URI string can be built using UriComponentsBuilder.build(), encoded using UriComponents.encode(), and sent using RestTemplate.exchange() like this:
public ResponseEntity<Object> requestRestServer()
{
HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(rawValidUrl)
.queryParams(
(LinkedMultiValueMap<String, String>) allRequestParams);
UriComponents uriComponents = builder.build().encode();
ResponseEntity<Object> responseEntity = restTemplate.exchange(uriComponents.toUri(), HttpMethod.GET,
entity, String.class);
return responseEntity;
}
Building, encoding, and extracting URI have been seperated out for clarity in the above code snippet.
You can URL encode the parameter values:
String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort=";
org.apache.commons.codec.net.URLCodec codec = new org.apache.commons.codec.net.URLCodec();
url1 = url1 + codec.encode("{\"price\":\"desc\"}");
OutputPage page = restTemplate.getForObject(url1, OutputPage.class);
You can set a specific UriTemplateHandler in your restTemplate. This handler would just ignore uriVariables :
UriTemplateHandler skipVariablePlaceHolderUriTemplateHandler = new UriTemplateHandler() {
#Override
public URI expand(String uriTemplate, Object... uriVariables) {
return retrieveURI(uriTemplate);
}
#Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
return retrieveURI(uriTemplate);
}
private URI retrieveURI(String uriTemplate) {
return UriComponentsBuilder.fromUriString(uriTemplate).build().toUri();
}
};
restTemplate.setUriTemplateHandler(skipVariablePlaceHolderUriTemplateHandler);
You can encode url before using RestTemplate
URLEncoder.encode(data, StandardCharsets.UTF_8.toString());
You can simply append a variable key to the URL and give the value using the restTemplate.getForObject() method.
Example:
String url = "http://example.com/api?key=12345&sort={data}";
String data="{\"price\":\"desc\"}";
OutputPage page = restTemplate.getForObject(url, OutputPage.class, data);
I want to develop a service that return a json file.
#RequestMapping(value = "/{fileName}/**", method = RequestMethod.GET, produces = { "application/json" })
public String jsonREST(#PathVariable String fileName) {
StringBuilder jsonBuilder = new StringBuilder();
logger.info("===> File name: " + fileName);
try {
BufferedReader bf = new BufferedReader(new FileReader("fileName + ".json"));
String line = null;
while ((line = bf.readLine()) != null) {
jsonBuilder.append(line);
}
} catch (Exception e) {
System.out.println("Error Parsing: - ");
}
return jsonBuilder.toString();
}
I need to get the path for example if the json file is in subdirectory or else.
use cases:
localhost:8080/my-directory/my-sub-dir/my-json-file
localhost:8080/my-json-file
Would you have any idea how I can get the hole path for example
my-directory/my-sub-dir/my-json-file
Or if you have another solution to do the same job, I will be very happy for that
Best regards
You can get the full request url by having Spring inject the HttpServletRequest and getting it as follows:
#RequestMapping(value = "/{fileName}/**", method = RequestMethod.GET, produces = { "application/json" })
public String jsonREST(HttpServletRequest request, #PathVariable String fileName) {
String uri = request.getRequestURI();
//Do your stuff here
}
Seems like you don't need a servlet container to achieve this. If I get what you are trying to do, you want to serve the json files statically. Try tweaking this:
https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
In short terms, I simplified the problem a lot. I am calling this code, and the response is received with status 200 (OK):
Receiver.java:
Response response = componentInstanceService.getResource(componentResourceType);
However, I don't know how can I retrieve the String contained in the body from this method:
Sender.java:
#Override
public Response getResource(ComponentResourceType resourceType) {
String path = getPath();
return Response.ok(this.getClass().getResourceAsStream(path)).build();
}
Please note that the communication between classes is working fine, as long as the Response is OK, however, how can I retrieve the String that Response contains?
This is what I would like to do roughly:
Receiver:
String result = componentInstanceService.getResource(componentResourceType);
The documentation for Response makes this pretty clear:
static Response.ResponseBuilder ok(java.lang.Object entity)
Create a new ResponseBuilder that contains a representation.
And:
abstract java.lang.Object getEntity()
Return the response entity.
In other words, the object you passed to Response.ok is the entity. You can retrieve it with the Response’s getEntity() method.
Obviously, you will need to cast it:
Response response = componentInstanceService.getResource(componentResourceType);
InputStream dataSource = (InputStream) response.getEntity();
Then you can read the stream as text. You haven’t mentioned the charset of your text files, so I’ll assume it’s UTF-8:
String result;
try (Scanner scanner = new Scanner(dataSource, StandardCharsets.UTF_8)) {
result = scanner.useDelimiter("\\z").next();
}
Update:
I suspected this might happen. You are returning a raw InputStream, which has no information about what type of data it is.
Change Sender.java to return a DataSource:
#Override
public DataSource getResource(ComponentResourceType resourceType) {
String path = getPath();
return new URLDataSource(this.getClass().getResource(path));
}
This way, the JAX-RS service will not only return HTTP 200 OK, but will also return a Content-Type header corresponding to the intuited type of your file.
You should then be able to invoke the method with:
DataSource dataSource = componentInstanceService.getResource(componentResourceType);
String result;
try (Scanner scanner = new Scanner(dataSource.getInputStream(), StandardCharsets.UTF_8)) {
result = scanner.useDelimiter("\\z").next();
}
There actually is a more robust way to read a DataSource. You can wrap it in a DataHandler:
DataSource dataSource = componentInstanceService.getResource(componentResourceType);
DataHandler handler = new DataHandler(dataSource);
DataFlavor flavor = DataFlavor.selectBestTextFlavor(
handler.getTransferDataFlavors());
if (flavor == null) {
// This should never happen with text files.
throw new IllegalArgumentException(
"Data has no flavors capable of supplying text.");
}
String result;
try (Reader reader = flavor.getReaderForText(handler)) {
StringBuilder s = new StringBuilder();
int c;
while ((c = reader.read()) >= 0) {
s.append((char) c);
}
result = s.toString();
} catch (UnsupportedFlavorException e) {
// Since we started with a flavor provided by the DataHandler,
// we should never get here.
throw new RuntimeException(e);
}
If you want to read the string from the body simply use
String result = componentInstanceService.getResource(componentResourceType).readEntity(String.class);
I'm using jackson, the scenario is:
In client (Android) generate url call to web service
The web service return "complex pojo" (has class members as other pojo) as json
Back in client map the json to pojo
I don't know how to complete this code to get things work although I searched all over and figured out the following start point, but then I completely stacked and I need help...
The "complex pojo":
public class MyPojo {
private List<MyOtherPojo> myOtherPojo;
private List<HashMap<String, String>> listOfMaps;
private Map<String, String> map;
//Constructors, getters & setters
}
The code at client:
String wsURI = "server.com/myservice?param1=a¶m2=b";
try {
URL url = new URL(wsURI);
URLConnection connection = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder sbuilder = new StringBuilder();
String aux = "";
while ( (aux = in.readLine()) != null) {
sbuilder.append(aux);
}
ObjectMapper mapper = new ObjectMapper();
//TODO get myPojo object from his representation as string at sbuilder.toString();
myPojo = ???;
} catch (Exception e) {}
SOLUTION thanks to #peeskillet:
MyPojo myPojo = mapper.readValue(connection.getInputStream(), MyPojo.class);