120

I want to call another web-api from my backend on a specific request of user. For example, I want to call Google FCM send message api to send a message to a specific user on an event.

Does Retrofit have any method to achieve this? If not, how I can do that?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Mahdi
  • 6,139
  • 9
  • 57
  • 109
  • 3
    You don't need a third-party library. Spring already has the [`RestTemplate`](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) – Paul Samsotha Feb 21 '17 at 11:24
  • Questions tagged [[resttemplate](https://stackoverflow.com/questions/tagged/resttemplate)] – Brent Bradburn Sep 09 '19 at 21:15
  • 5
    [RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) will be deprecated in a future version, use the more modern alternative [WebClient](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html) – jumping_monkey Oct 24 '19 at 11:05

10 Answers10

172

This website has some nice examples for using spring's RestTemplate. Here is a code example of how it can work to get a simple object:

private static void getEmployees()
{
    final String uri = "http://localhost:8080/springrestexample/employees.xml";

    RestTemplate restTemplate = new RestTemplate();
    String result = restTemplate.getForObject(uri, String.class);

    System.out.println(result);
}
Torsten N.
  • 2,051
  • 1
  • 12
  • 17
  • 1
    *Object result = restTemplate.getForObject(uri, Object .class);* - to be more generic – Muhammad Faizan Uddin Dec 25 '18 at 08:43
  • @Muhammad Faizan Uddin I thought about it, but iirc that might now work if the Object can not be serialized properly for any reason; while the string approach always works because JSON can always be serialized into a string. – Torsten N. Jan 04 '19 at 08:36
  • 21
    [RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) will be deprecated in a future version, use the more modern alternative [WebClient](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html) – jumping_monkey Oct 24 '19 at 11:05
  • 3
    [Added an answer](https://stackoverflow.com/a/63445248/2237467) below for WebClient. – Snackoverflow Oct 19 '20 at 05:18
  • it's realy very nice tutorial – sumit katiyar Feb 09 '21 at 06:32
  • Maybe add the dependency import would be even better: `import org.springframework.web.client.RestTemplate;` – Karl Xu Mar 28 '22 at 11:37
62

Modern Spring 5+ answer using WebClient instead of RestTemplate.

Configure WebClient for a specific web-service or resource as a bean (additional properties can be configured).

@Bean
public WebClient localApiClient() {
    return WebClient.create("http://localhost:8080/api/v3");
}

Inject and use the bean from your service(s).

@Service
public class UserService {

    private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(3);

    private final WebClient localApiClient;

    @Autowired
    public UserService(WebClient localApiClient) {
        this.localApiClient = localApiClient;
    }

    public User getUser(long id) {
        return localApiClient
                .get()
                .uri("/users/" + id)
                .retrieve()
                .bodyToMono(User.class)
                .block(REQUEST_TIMEOUT);
    }

}
noob_nerd
  • 531
  • 1
  • 6
  • 21
Snackoverflow
  • 5,332
  • 7
  • 39
  • 69
  • 26
    For those of you who are searching for the package which includes WebClient, it is `spring-boot-starter-webflux` in `org.springframework.boot`. You have to include that in your pom.xml file. – ersu Apr 26 '21 at 21:16
  • 1
    Whoever found @ersu's comment useful also found this useful ;) https://stackoverflow.com/a/60747437/413032. – Davut Gürbüz Oct 06 '21 at 15:58
  • Is it ok for someone to use webclient reactive api calls with blocking behavior (given above) or does he has to have reactive components all over the application? Some suggests former is anti-pattern or something. (I have very little subject knowledge on this one.) @ersu – yokus Nov 29 '22 at 14:03
  • @yokus IMO, for most simple cases, it's okay to use it the way shown above, having the timeout come from a configuration file/server. You probably want to mess with async calls and take advantage of the reactive API if your app is performing slowly and requires optimization, usually involving several requests. E.g, instead of 3 sequential blocking requests, which each take 500ms, if these requests are independent of each-other, you could perform 3 parallel requests, reducing processing time from 1500ms to 500ms. – Snackoverflow Nov 29 '22 at 14:48
16

Instead of String you are trying to get custom POJO object details as output by calling another API/URI, try the this solution. I hope it will be clear and helpful for how to use RestTemplate also,

In Spring Boot, first we need to create Bean for RestTemplate under the @Configuration annotated class. You can even write a separate class and annotate with @Configuration like below.

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
       return builder.build();
    }
}

Then, you have to define RestTemplate with @Autowired or @Injected under your service/Controller, whereever you are trying to use RestTemplate. Use the below code,

@Autowired
private RestTemplate restTemplate;

Now, will see the part of how to call another api from my application using above created RestTemplate. For this we can use multiple methods like execute(), getForEntity(), getForObject() and etc. Here I am placing the code with example of execute(). I have even tried other two, I faced problem of converting returned LinkedHashMap into expected POJO object. The below, execute() method solved my problem.

ResponseEntity<List<POJO>> responseEntity = restTemplate.exchange(
    URL, 
    HttpMethod.GET, 
    null, 
    new ParameterizedTypeReference<List<POJO>>() {
    });
List<POJO> pojoObjList = responseEntity.getBody();

Happy Coding :)

BlackMarker
  • 139
  • 12
Nallamachu
  • 1,420
  • 18
  • 34
  • So when I try to use almost your exact code, I get the error "Cannot deserialise instance of [my pojo class] out of start object token. Do you know why this would be? – lek Oct 16 '20 at 14:01
  • Kindly verify your pojo implements Serializable interface or not? if not implement it and try. – Nallamachu Oct 17 '20 at 11:23
  • Unfortunately, that did not resolve it, thank you anyway. – lek Oct 19 '20 at 21:53
6

Create Bean for Rest Template to auto wiring the Rest Template object.

@SpringBootApplication
public class ChatAppApplication {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ChatAppApplication.class, args);
    }

}

Consume the GET/POST API by using RestTemplate - exchange() method. Below is for the post api which is defined in the controller.

@RequestMapping(value = "/postdata",method = RequestMethod.POST)
    public String PostData(){

       return "{\n" +
               "   \"value\":\"4\",\n" +
               "   \"name\":\"David\"\n" +
               "}";
    }

    @RequestMapping(value = "/post")
    public String getPostResponse(){
        HttpHeaders headers=new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<String> entity=new HttpEntity<String>(headers);
        return restTemplate.exchange("http://localhost:8080/postdata",HttpMethod.POST,entity,String.class).getBody();
    }

Refer this tutorial[1]

[1] https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm

Senthuran
  • 1,583
  • 2
  • 15
  • 19
5

As has been mentioned in the various answers here, WebClient is now the recommended route. You can start by configuring a WebClient builder:

@Bean
public WebClient.Builder getWebClientBuilder(){
    return WebClient.builder();
}

Then inject the bean and you can consume an API as follows:

@Autowired
private WebClient.Builder webClientBuilder;


Product product = webClientBuilder.build()
            .get()
            .uri("http://localhost:8080/api/products")
            .retrieve()
            .bodyToMono(Product.class)
            .block();
Fred
  • 332
  • 4
  • 5
  • 1
    How can I change this: 1. Direct pass thru without serialising @RequestBody, 2. Return a custom ResponseEntity without expecting a specific shape from `.bodyToMono`. So, that I can only do authentication in middleware server but passes through request to internal services – bh4r4th Oct 14 '22 at 02:19
2

Does Retrofit have any method to achieve this? If not, how I can do that?

YES

Retrofit is type-safe REST client for Android and Java. Retrofit turns your HTTP API into a Java interface.

For more information refer the following link

https://howtodoinjava.com/retrofit2/retrofit2-beginner-tutorial

Dharman
  • 30,962
  • 25
  • 85
  • 135
Madhu Tomy
  • 662
  • 11
  • 25
1

In this case need download whit my API, files hosted in other server.

In my case, don't need use a HTTP client to download the file in a external URL, I combined several answers and methods worked in previous code for files that were in my local server.

My code is:

@GetMapping(value = "/download/file/pdf/", produces = MediaType.APPLICATION_PDF_VALUE)
    public ResponseEntity<Resource> downloadFilePdf() throws IOException {
        String url = "http://www.orimi.com/pdf-test.pdf";
    
        RestTemplate restTemplate = new RestTemplate();
        byte[] byteContent = restTemplate.getForObject(url, String.class).getBytes(StandardCharsets.ISO_8859_1);
        InputStream resourceInputStream = new ByteArrayInputStream(byteContent);
    
        return ResponseEntity.ok()
                .header("Content-disposition", "attachment; filename=" + "pdf-with-my-API_pdf-test.pdf")
                .contentType(MediaType.parseMediaType("application/pdf;"))
                .contentLength(byteContent.length)
                .body(new InputStreamResource(resourceInputStream));
    }

and it works with HTTP and HTTPS urls!

  • The accepted answer already shows how to achieve the desired result using Spring's `RestTemplate`. How is your code different? – slauth Jul 29 '21 at 23:01
1

Since the question explicitly tags spring-boot, it worth noting that recent versions already ship a pre-configured instance of a builder for WebClient, thus you can directly inject it inside your service constructor without the needing to define a custom bean.

@Service
public class ClientService {

    private final WebClient webClient;

    public ClientService(WebClient.Builder webClientBuilder) {
        webClient = webClientBuilder
                        .baseUrl("https://your.api.com")
    }

    //Add all the API call methods you need leveraging webClient instance

}

https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-webclient.html

4javier
  • 481
  • 2
  • 7
  • 22
0

Simplest way I have found is to:

  • Create an annotated interface (or have it generated from somehing like OpenAPI)
  • Give that interface to Spring RestTemplate Client

The Spring RestTemplate Client will parse the annotations on the interface and give you a type safe client, a proxy-instance. Any invocation on the methods will be seamlessly translated to rest-calls.

final MyApiInterface myClient = SpringRestTemplateClientBuilder
  .create(MyApiInterface.class)
  .setUrl(this.getMockUrl())
  .setRestTemplate(restTemplate)         // Optional
  .setHeader("header-name", "the value") // Optional
  .setHeaders(HttpHeaders)               // Optional
  .build();

And a rest call is made by inoking methods, like:

final ResponseEntity<MyDTO> response = myClient.getMyDto();
Tomas Bjerre
  • 3,270
  • 22
  • 27
-1

3 steps:

  1. install webflux
  2. configure it as Bean
  3. use it in service by Dependency Injection
  • install webflux
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
  • configure it as Bean
package com.scloud.productservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication()
public class ProductServiceApplication {

    @Bean
    public WebClient webClient() {
        return WebClient.create();
    }

    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }

}

  • use it in service by Dependency Injection
@Service
@RequiredArgsConstructor
@Slf4j
public class EmployeeServiceImpl implements EmployeeService {
    private final WebClient webClient;
    private final String url = "https://jsonplaceholder.typicode.com/users";

    public Flux<Employee> findAll() {
        Flux<Employee> employees = webClient.get()
                .uri(url)
                .retrieve()
                .bodyToFlux(Employee.class)
                .timeout(Duration.ofMillis(10_000));

        employees.subscribe(System.out::println);

        return employees;
    }

employee

@Document(value = "employee") // @Document annotation is used to indicate that a class is a MongoDB document.
@AllArgsConstructor
@NoArgsConstructor
@Builder // @Builder lets you automatically produce the code required to have your class be instantiable with code such as:
         // Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
         // The builder will be named after the class name, in this case Person, and will have a single constructor that takes all of the required fields in the class, in this case name, city, and job. The builder will have methods to set any of the fields in the class, but will not have any methods to get back the state of the object - this can be accessed once built with the build() method.
@Data // @Data is a convenient shortcut annotation that bundles the features of @ToString, @EqualsAndHashCode, @Getter / @Setter and @RequiredArgsConstructor together
public class Employee {
    @Id
    private String id;

    @NotEmpty(message = "Name must not be null or empty!")
    private String name;
    @NotEmpty(message = "Description must not be null or empty!")
    private String email;
    @NotNull(message = "Name must not be null or empty!")
    private String website; // BigDecimal is used for financial calculations, it is the best choice.
}

employeeService

public interface EmployeeService {
    Flux<Employee> findAll();

    Mono<Employee> findById(Integer id);

    Mono<Employee> create(Employee e);

    Mono<Employee> update(Employee e);

    Mono<Void> delete(Integer id);
}

@Service
@RequiredArgsConstructor
@Slf4j
public class EmployeeServiceImpl implements EmployeeService {
    private final WebClient webClient;
    private final String url = "https://jsonplaceholder.typicode.com/users";

    public Flux<Employee> findAll() {
        Flux<Employee> employees = webClient.get()
                .uri(url)
                .retrieve()
                .bodyToFlux(Employee.class)
                .timeout(Duration.ofMillis(10_000));

        employees.subscribe(System.out::println);

        return employees;
    }

.......


Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/employees")
public class EmployeeController {
    private final EmployeeService employeeService;

    @GetMapping()
    @ResponseStatus(HttpStatus.OK)
    public Flux<Employee> findAll() {
        return employeeService.findAll();
    }
....
}