Cree una API RESTFul con Spring Boot

En este artículo, aprenderemos cómo crear API RESTFul en la aplicación web Spring Boot utilizando una arquitectura en capas y un enfoque de desarrollo basado en pruebas.

Índice
  1. configuración del proyecto
  2. Habilitar MVC web
  3. Creación de API RESTFul
  4. Prueba unitaria para las clases de controlador y servicio

configuración del proyecto

Para la configuración inicial de su proyecto Spring Boot, debe usar Spring Initializr. Elegir el lona de primavera adiccion.

proyecto experto

Puede hacer clic en el enlace a continuación para generar un proyecto Maven con dependencias preseleccionadas:

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.5.0.RELEASE&packaging=jar&jvmVersion=11&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com. ejemplo.demo&dependencias=web

un tipico pom.xml archivo para un proyecto web se ve así: -

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>api</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>api</name>
	<description>Create Feign Client to consume RESTFul APIs</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Proyecto Gradle

Del mismo modo, puede hacer clic en el enlace a continuación para generar un proyecto Gradle con dependencias preseleccionadas:

https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.5.0.RELEASE&packaging=jar&jvmVersion=11&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com. ejemplo.demo&dependencias=web

un tipico construir.gradle archivo para un proyecto web se ve así: -

plugins {
  id 'org.springframework.boot' version '2.5.0'
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
  useJUnitPlatform()
}

Habilitar MVC web

Cuando trabaja con el proyecto Spring Boot, no tiene que hacer nada para habilitar Spring Web MVC para su proyecto. Asegurarse:-

  1. Teneis spring-boot-starter-web adicción en tu pom.xml Donde construir.gradle
  2. Usted utiliza @SpringBootApplication en el archivo de clase de inicio de su aplicación.

Se recomienda Spring Boot, cuando ve la dependencia web en el classpath, configura toda la configuración predeterminada necesaria para el desarrollo de API para que pueda concentrarse en su lógica comercial.

package com.example.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiApplication {

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

}

Creación de API RESTFul

Usamos arquitectura en capas para crear API RESTFul donde la capa del controlador es la primera línea para responder a la solicitud de API y delegar la solicitud a la capa de servicio, luego a la capa de repositorio o cliente, etc. Una vez que los datos solicitados están disponibles, se devuelven en el mismo orden, como para el repositorio o el cliente al servicio de la capa del controlador.

Capa de controlador

Creamos un UserController class para crear API RESTFul para operaciones CRUD. Pocas cosas para entender: -

  1. Usar @RestController a nivel de clase, ayuda a vincular los convertidores HTTP predeterminados por usted, por ejemplo, cuando devuelve un User objeto de los métodos del controlador, éste se encarga de convertirlos en JSON.
  2. Usar @RequestMapping a nivel de clase, para asignar API a URL.
  3. Usa la abreviatura de @RequestMapping es decir @GetMapping, @PostMapping, @PutMapping, @DeleteMapping a nivel de método.
  4. Usar @ResponseStatus a nivel del método de Código de estadísticas HTTP.
  5. Delegue el trabajo a Service Layer, aquí usando UserService
package com.example.api.controller;

@RestController
@RequestMapping("/users")
public class UserController {

	@Autowired
	private UserService userService;

	@GetMapping
	public Users getAllUsers() {
		return userService.getAllUsers();
	}

	@GetMapping("/{id}")
	public User getUserById(@PathVariable Long id) {
		return userService.getUserById(id);
	}

	@PostMapping
	@ResponseStatus(HttpStatus.CREATED)
	public Long createUser(User user) {
		return userService.createUser(user);
	}

	@PutMapping("/{id}")
	@ResponseStatus(HttpStatus.OK)
	public void updateUser(@PathVariable Long id, User user) {
		userService.updateUser(id, user);
	}

	@DeleteMapping("/{id}")
	@ResponseStatus(HttpStatus.OK)
	public void deleteUserById(@PathVariable Long id) {
		userService.deleteUserById(id);
	}
}

capa de servicio

Creamos un UserService clase que se ocupa de la lógica empresarial. Pocas cosas para entender: -

  1. Usar @Service a nivel de clase, que registra automáticamente el bean de clase de servicio en el contexto de arranque de primavera.
  2. La clase de servicio puede extraer más datos de: -
    • Clases de repositorio (utilizadas para recuperar datos de la base de datos al extender Spring Boot JpaRepository)
    • Clases de cliente (utilizadas para recuperar datos de API de terceros mediante FeignClient o RestTemplate)
    • Cualquier otro proveedor de datos

En este ejemplo, nuestro UserService usos UserMockClient para recuperar datos ficticios.

package com.example.api.service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMockClient userMockClient;

    @Override
    public List<User> getAllUsers() {
        return userMockClient.getAllUsers();
    }

    @Override
    public User getUserById(Long id) {
        return userMockClient.getUserById(id);
    }

    @Override
    public Long createUser(User user) {
        return userMockClient.createUser(user);
    }

    @Override
    public boolean updateUser(Long id, User user) {
        return userMockClient.updateUser(id, user);
    }

    @Override
    public boolean deleteUserById(Long id) {
        return userMockClient.deleteUserById(id);
    }
}

Eso es. Simplemente inicie su aplicación y pruebe sus API.

Prueba unitaria para las clases de controlador y servicio

Se recomienda que escriba casos de prueba de unidad para las clases de controlador y servicio que ha creado.

Prueba unitaria para la clase de controlador

Vamos a crear una clase UserControllerTest dentro src/test/java directorio y sigue la misma estructura de paquete que nuestro UserController clasificar. Cosas a tener en cuenta que: -

  1. Hemos utilizado @SpringBootTest Una anotación a nivel de clase que configura un contexto de arranque de primavera de su aplicación antes de ejecutar casos de prueba. Esto nos da la opción de vincular el frijol de resorte real usando @Autowired y burlarse del frijol de primavera usando @MockBean anotación.
  2. nosotros autocableamos MockMvc y se burló UserService clase en este ejemplo.
  3. Hemos utilizado @AutoConfigureMockMvc anotación de nivel de clase que se configura automáticamente MockMvc que se utiliza para realizar operaciones HTTP utilizando el marco Mockito.

El marco Mockito se utiliza para simular el comportamiento de las clases. En este ejemplo, nos burlamos de la capa de servicio y las operaciones HTTP.

En cada caso de prueba de la capa del controlador: -

  1. Primero simulamos el comportamiento de la capa de servicio usando métodos de Mockito
  2. luego realizamos una operación HTTP usando MockMvc que delegan la solicitud a la capa de servicio simulada en lugar de a la capa real.
  3. entonces afirmamos con la respuesta esperada
package com.example.api.controller;

@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void getAllUsers_whenValidRequest_returnsValidResponse() throws Exception {
		// mock service class behavior
        when(userService.getAllUsers()).thenReturn(UserTestData.users());

        // perform HTTP operation using MockMvc
        mockMvc.perform(get("/users"))
                .andExpect(status().is2xxSuccessful())
                .andExpect(jsonPath("$", hasSize(UserTestData.users().size())))
                .andExpect(jsonPath("$[0].id", equalTo(1)))
                .andExpect(jsonPath("$[0].name", equalTo("Adam")))
                .andExpect(jsonPath("$[0].dateOfBirth", equalTo("22 Aug 1986")));
    }

    @Test
    public void getAllUsers_whenServiceThrowException_returnsInternalServerError() throws Exception {
        when(userService.getAllUsers()).thenThrow(new RuntimeException("Oops, Something went wrong!"));

        mockMvc.perform(get("/users"))
                .andExpect(status().isInternalServerError())
                .andExpect(jsonPath("$.message", equalTo("Internal Server Error")))
                .andExpect(jsonPath("$.debugMessage", equalTo("Oops, Something went wrong!")));
    }

    @Test
    public void getUserById_whenValidUserId_returnThatUser() throws Exception {
        when(userService.getUserById(anyLong())).thenReturn(UserTestData.user());

        mockMvc.perform(get("/users/1"))
                .andExpect(status().is2xxSuccessful())
                .andExpect(jsonPath("$.id", equalTo(1)))
                .andExpect(jsonPath("$.name", equalTo("Adam")))
                .andExpect(jsonPath("$.dateOfBirth", equalTo("22 Aug 1986")));
    }

    @Test
    public void createUser_whenValidUserData_createAndReturnTheUserId() throws Exception {
        when(userService.createUser(UserTestData.user())).thenReturn(UserTestData.user().getId());

        mockMvc.perform(post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(asJsonString(UserTestData.user())))
                .andExpect(status().is2xxSuccessful());
    }

    @Test
    public void updateUser_whenValidUserId_updateThatUser() throws Exception {

        mockMvc.perform(put("/users/1"))
                .andExpect(status().is2xxSuccessful());
    }

    @Test
    public void deleteUser_whenValidUserId_deleteThatUser() throws Exception {

        mockMvc.perform(delete("/users/1"))
                .andExpect(status().is2xxSuccessful());
    }

    @Test
    public void patchUser_whenUnsupportedHttpVerb_returnsMethodNotAllowed() throws Exception {
        mockMvc.perform(patch("/users"))
                .andExpect(status().isMethodNotAllowed())
                .andExpect(jsonPath("$.message", equalTo("Method Not Allowed")))
                .andExpect(jsonPath("$.debugMessage", equalTo("Request method 'PATCH' not supported")));
    }

    public static String asJsonString(final Object obj) {
        try {
            return new ObjectMapper().writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Prueba unitaria para clase de servicio

Vamos a crear una clase UserServiceTest dentro src/test/java directorio y sigue la misma estructura de paquete que nuestro UserService clasificar.

Aquí hemos vuelto a utilizar @SpringBootTest anotación para vincular el frijol de primavera real de UserService y el frijol de primavera simulado de la capa del cliente UserMockClient.

En cada caso de prueba de capa de servicio: -

  1. Primero simulamos el comportamiento de la capa del cliente usando métodos de Mockito.
  2. luego llamamos al método de la capa de servicio que delega la llamada a la capa de cliente ficticia en lugar de a la capa real.
  3. entonces afirmamos con la salida esperada
package com.example.api.service;

@SpringBootTest
public class UserServiceTest {

    @MockBean
    private UserMockClient userMockClient;

    @Autowired
    private UserService userService;

    @Test
    public void getAllUsers_whenValidProviderResponse_returnAllUsers() {
        when(userMockClient.getAllUsers()).thenReturn(UserTestData.users());

        List<User> users = userService.getAllUsers();

        assertThat(users.size()).isEqualTo(1);
        assertThat(users.get(0).getId()).isEqualTo(1);
        assertThat(users.get(0).getName()).isEqualTo("Adam");
        assertThat(users.get(0).getDateOfBirth().toString()).isEqualTo("1986-08-22");
    }

    @Test
    public void getUserById_whenValidUserId_returnThatUser() {
        when(userMockClient.getUserById(anyLong())).thenReturn(UserTestData.user());

        User user = userService.getUserById(1L);

        assertThat(user.getId()).isEqualTo(1);
        assertThat(user.getName()).isEqualTo("Adam");
        assertThat(user.getDateOfBirth().toString()).isEqualTo("1986-08-22");
    }

    @Test
    public void createUser_whenValidUserData_createAndReturnUserId() {
        when(userMockClient.createUser(any(User.class))).thenReturn(UserTestData.user().getId());

        Long id = userService.createUser(UserTestData.user());

        assertThat(id).isEqualTo(1L);
    }

    @Test
    public void updateUser_whenValidUserData_updateAndReturnStatus() {
        when(userMockClient.updateUser(anyLong(), any(User.class))).thenReturn(true);

        Boolean status = userService.updateUser(UserTestData.user().getId(), UserTestData.user());

        assertThat(status).isTrue();
    }

    @Test
    public void deleteUser_whenValidUserId_deleteAndReturnStatus() {
        when(userMockClient.deleteUserById(anyLong())).thenReturn(true);

        Boolean status = userService.deleteUserById(UserTestData.user().getId());

        assertThat(status).isTrue();
    }
}

Descargue el código fuente completo para este ejemplo desde github/springboot-api

Si quieres conocer otros artículos parecidos a Cree una API RESTFul con Spring Boot puedes visitar la categoría Tutoriales.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Esta página web utiliza cookies para analizar de forma anónima y estadística el uso que haces de la web, mejorar los contenidos y tu experiencia de navegación. Para más información accede a la Política de Cookies . Ver mas