Mahdi
Mahdi

Reputation: 6389

Call another rest api from my server in Spring-Boot

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?

Upvotes: 128

Views: 444620

Answers (10)

s00103898-276165-15433
s00103898-276165-15433

Reputation: 988

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

Upvotes: -1

Tomas Bjerre
Tomas Bjerre

Reputation: 3500

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

Upvotes: 0

4javier
4javier

Reputation: 639

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

Upvotes: 1

Jhonatan_Celis
Jhonatan_Celis

Reputation: 21

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!

Upvotes: 1

Fred
Fred

Reputation: 352

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

Upvotes: 7

Snackoverflow
Snackoverflow

Reputation: 6271

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

}

Upvotes: 70

Madhu Tomy
Madhu Tomy

Reputation: 782

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

Upvotes: 2

Senthuran
Senthuran

Reputation: 1837

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

Upvotes: 6

Nallamachu
Nallamachu

Reputation: 1488

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

Upvotes: 16

Torsten N.
Torsten N.

Reputation: 2141

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

Upvotes: 181

Related Questions