Como Criar Endpoints - ecosif-auth
Este guia explica como criar novos endpoints REST no ecosif-auth seguindo os padrões do projeto.
Estrutura Básica
1. Criar Controller
Crie uma nova classe Controller no pacote io.ecosif.auth.controller:
package io.ecosif.auth.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/api/exemplo")
@Tag(name = "Exemplo", description = "Endpoints de exemplo")
public class ExemploController {
@GetMapping
@Operation(summary = "Listar exemplos", description = "Retorna lista de exemplos")
public ResponseEntity<?> listar() {
log.info("Listando exemplos");
// Implementação
return ResponseEntity.ok().build();
}
}
2. Criar DTO (se necessário)
Crie DTOs no pacote io.ecosif.auth.dto:
package io.ecosif.auth.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@Schema(description = "DTO de exemplo")
public class ExemploDTO {
@NotBlank(message = "Nome é obrigatório")
@Schema(description = "Nome do exemplo", example = "Exemplo 1", required = true)
private String nome;
@Schema(description = "Descrição do exemplo", example = "Descrição do exemplo")
private String descricao;
}
3. Criar Service (se necessário)
Crie Services no pacote io.ecosif.auth.service:
package io.ecosif.auth.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@Transactional
public class ExemploService {
public void processar(String nome) {
log.info("Processando: {}", nome);
// Lógica de negócio
}
}
Padrões e Convenções
Anotações Swagger/OpenAPI
Documente endpoints com anotações Swagger:
@PostMapping
@Operation(
summary = "Criar exemplo",
description = "Cria um novo exemplo no sistema"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Criado com sucesso",
content = @Content(schema = @Schema(implementation = ExemploDTO.class))
),
@ApiResponse(
responseCode = "400",
description = "Dados inválidos",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
)
})
public ResponseEntity<?> criar(@Valid @RequestBody ExemploDTO dto) {
// ...
}
Validação
Valide DTOs usando Bean Validation:
@PostMapping
public ResponseEntity<?> criar(
@Valid @RequestBody ExemploDTO dto
) {
// DTO já foi validado automaticamente
// Se inválido, retorna 400 Bad Request automaticamente
}
Tratamento de Erros
Use exceções customizadas para erros de negócio:
@PostMapping
public ResponseEntity<?> criar(@Valid @RequestBody ExemploDTO dto) {
try {
exemploService.processar(dto);
return ResponseEntity.ok().build();
} catch (BadRequestException e) {
throw e; // Será tratado pelo GlobalExceptionHandler
}
}
Logging
Use @Slf4j para logging:
@Slf4j
@RestController
public class ExemploController {
@PostMapping
public ResponseEntity<?> criar(@Valid @RequestBody ExemploDTO dto) {
log.info("Criando exemplo: {}", dto.getNome());
// ...
log.debug("Detalhes: {}", dto); // Use DEBUG para informações detalhadas
// ...
if (erro) {
log.warn("Aviso: {}", mensagem);
}
if (erroGrave) {
log.error("Erro ao processar: {}", dto, exception);
}
}
}
Autenticação
Para proteger endpoints, eles já são protegidos por padrão. O token JWT é validado automaticamente pelo TokenAuthenticationFilter.
Para acessar o usuário autenticado:
@GetMapping("/meu-perfil")
public ResponseEntity<?> meuPerfil(
@AuthenticationPrincipal LocalUser localUser
) {
User user = localUser.getUser();
// Usar informações do usuário
}
Ou use a anotação customizada:
import io.ecosif.auth.config.CurrentUser;
@GetMapping("/meu-perfil")
public ResponseEntity<?> meuPerfil(
@CurrentUser LocalUser localUser
) {
User user = localUser.getUser();
}
Códigos HTTP
Use códigos HTTP apropriados:
200 OK: Sucesso201 Created: Criado com sucesso400 Bad Request: Dados inválidos401 Unauthorized: Não autenticado403 Forbidden: Não autorizado404 Not Found: Recurso não encontrado500 Internal Server Error: Erro interno
Exemplo Completo
Controller
package io.ecosif.auth.controller;
import io.ecosif.auth.dto.ApiResponse;
import io.ecosif.auth.dto.ExemploDTO;
import io.ecosif.auth.exception.BadRequestException;
import io.ecosif.auth.service.ExemploService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Slf4j
@RestController
@RequestMapping("/api/exemplo")
@Tag(name = "Exemplo", description = "Endpoints de exemplo")
public class ExemploController {
private final ExemploService exemploService;
public ExemploController(ExemploService exemploService) {
this.exemploService = exemploService;
}
@GetMapping
@Operation(
summary = "Listar exemplos",
description = "Retorna lista de exemplos"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Lista obtida com sucesso"
)
})
public ResponseEntity<?> listar() {
log.info("Listando exemplos");
// Implementação
return ResponseEntity.ok().build();
}
@PostMapping
@Operation(
summary = "Criar exemplo",
description = "Cria um novo exemplo no sistema"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "201",
description = "Criado com sucesso",
content = @Content(schema = @Schema(implementation = ExemploDTO.class))
),
@ApiResponse(
responseCode = "400",
description = "Dados inválidos",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
)
})
public ResponseEntity<ExemploDTO> criar(
@Valid @RequestBody ExemploDTO dto
) {
log.info("Criando exemplo: {}", dto.getNome());
try {
exemploService.processar(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(dto);
} catch (BadRequestException e) {
throw e; // Tratado pelo GlobalExceptionHandler
}
}
@GetMapping("/{id}")
@Operation(
summary = "Obter exemplo por ID",
description = "Retorna um exemplo específico pelo ID"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Exemplo encontrado",
content = @Content(schema = @Schema(implementation = ExemploDTO.class))
),
@ApiResponse(
responseCode = "404",
description = "Exemplo não encontrado"
)
})
public ResponseEntity<ExemploDTO> obterPorId(@PathVariable Long id) {
log.info("Buscando exemplo com ID: {}", id);
// Implementação
return ResponseEntity.ok().build();
}
}
Service
package io.ecosif.auth.service;
import io.ecosif.auth.dto.ExemploDTO;
import io.ecosif.auth.exception.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@Transactional
public class ExemploService {
public void processar(ExemploDTO dto) {
log.info("Processando exemplo: {}", dto.getNome());
// Validações de negócio
if (dto.getNome().length() < 3) {
throw new BadRequestException("Nome deve ter pelo menos 3 caracteres");
}
// Lógica de negócio
// ...
}
}
DTO
package io.ecosif.auth.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Data
@Schema(description = "DTO de exemplo")
public class ExemploDTO {
@NotBlank(message = "Nome é obrigatório")
@Size(min = 3, max = 100, message = "Nome deve ter entre 3 e 100 caracteres")
@Schema(description = "Nome do exemplo", example = "Exemplo 1", required = true)
private String nome;
@Schema(description = "Descrição do exemplo", example = "Descrição do exemplo")
private String descricao;
}
Testes
Crie testes para seus endpoints:
package io.ecosif.auth.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class ExemploControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
void should_CreateExample_When_ValidData() throws Exception {
ExemploDTO dto = new ExemploDTO();
dto.setNome("Teste");
dto.setDescricao("Descrição do teste");
mockMvc.perform(post("/api/exemplo")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(dto)))
.andExpect(status().isCreated());
}
}
Documentação OpenAPI
Após criar o endpoint, a documentação será automaticamente gerada e disponível em:
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/v3/api-docs
Checklist
Antes de considerar o endpoint completo:
- ✅ Controller criado com anotações adequadas
- ✅ DTOs criados e validados
- ✅ Service implementado (se necessário)
- ✅ Endpoint documentado com Swagger
- ✅ Logs apropriados adicionados
- ✅ Tratamento de erros implementado
- ✅ Testes criados
- ✅ Código revisado