Introdução
REST API REST 100%功能性PostgreSQL数据库,Spring数据JPA参数,作为咨询机构,DTO数据包,Duals数据集,dadas总线数据集, MockMVC和Swagger的形式化文档,集成了有效的Nossos端点证明。
完全免费的Github播客:
Clone no Github
Dependências
正如dependênciasna nossaaplicaçãosão:
- 春季靴冬眠春季数据JPA模拟MVC昂首阔步
Todasserãotratadas pelo Maven,segundo o pom.xml abaixo:
<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> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.6.1</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.6.1</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency><dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency></dependencies>
Spring boot
春季靴子是必需的,但必须由执行官签发,但必须由执行官负责,并必须执行生产性保护措施。
Java的Para isso basta criarmos uma classe com ométodomain:
@SpringBootApplication
public class Main {public static void main(String[] args) {SpringApplication.run(Main.class, args);}
}
Hibernate e Spring data JPA
春季数据联合使用的冬眠和冬日课程。
Primeiramente,vamos criar uma classe Produto.java可以用作在地图上实现现实的必要工具。
As anotações de @ApiModelProperty serão usadas para a documentação com o Swagger, que veremos mais a frente.@Entity
@Table(name = "produto")
public class Produto {@Id@SequenceGenerator(name = "produto_seq", sequenceName = "produto_seq", allocationSize = 1)@GeneratedValue(generator = "produto_seq", strategy = GenerationType.AUTO)private int id;private String nome;private double valor;@ApiModelProperty(notes = "Identificador do produto")public int getId() {return id;}public void setId(int id) {this.id = id;}@ApiModelProperty(notes = "Nome do produto")public String getNome() {return nome;}public void setNome(String nome) {this.nome = nome;}@ApiModelProperty(notes = "Valor do produto")public double getValor() {return valor;}public void setValor(double valor) {this.valor = valor;}}
Depois criaremos um'Repository',春季数据,JPA que facilita ainda mais,作为banco de dados顾问,alémde serútilmais a frente durante os证明人,ondeserápossívelcriar unsrepositóbandecaçãoque n dados durante essa etapa。
没有储存库,乌鲁木齐,多米尼加共和国找到所有()您可以通过以下方式访问您的网站:Passando um objeto depaginação,somente pelo nome do medodo,seráinferido uma busca por todos os elementos,e ummétodode busca com uma uma e a customizada。
O 'Repository' são interfaces, que o Spring irá tratar como injeção de dependências quando forem invocadas mais a frente.@Repository
public interface ProdutoRepository extends PagingAndSortingRepository<Produto, Integer> {public Page<Produto> findAll(Pageable pageable);@Query("SELECT p FROM Produto p "+ "WHERE lower(nome) like %:busca% ")public Page<Produto> busca(@Param("busca") String busca, Pageable pageable);}
编辑arquivo application.properties,然后在com上进行配置,在api上配置,然后在caminhopadrão和modo deinicialização中进行配置。 不使用PostgreSQL的Devemos criartambémo banco de dados e um esquema。
#debug
debug=true#api
server.servlet.context-path=/api#conexão
javax.persistence.create-database-schemas=true
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/teste
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.default_schema=api#define como o hibernate irá se comportar quanto a criação do esquema
#create: Apaga e recria todo o esquema
#update: Atualiza o mapeamento
spring.jpa.hibernate.ddl-auto=update#define se irá executar o 'data.sql'
#always: Sempre irá executar o data.sql
#never: Nunca irá executar o data.sql
spring.datasource.initialization-mode=never#codificação do data.sql
spring.datasource.sqlScriptEncoding=UTF-8
Controlador REST
Para disponibilizar nossa API publica,PromotoController.java para colocar todos os端点引用了essa class。 E com作为春季必不可少的东西。
阿诺塔@RequestMapping(“ / produtos”)irádefinir o caminhopadrãodesse端点,nesse caso / produtos。
准入门槛的端点许可,反作用的Ang角,precisamos habilitar或CORS(跨来源资源共享),一个反义词@CrossOrigin。
As anotações @Api e @ApiOperation servirão para documenta pelo swagger, que veremos mais a frente.@RestController
@RequestMapping("/produtos")
@CrossOrigin
@Api(tags = "Produtos", description = "API de produtos")
public class ProdutoController {private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());@AutowiredProdutoRepository produtoRepository;...}
阿西玛(Acima),控制权的基础设施建设,必要的备件,生产资料的备件,生产资料的储备金,春天的依赖关系。
GET
可以在vamos crimar的首要端点处找到要处理的产品,并通过@GetMapping()通知响应者来获取GET请求。 Defino osparâmetrosque essa URL,podo receber,todos opcionais,para que possa paginar,ordenar e fazer uma busca。
@ApiOperation(value = "Lista os produtos")@GetMapping()public Page<Produto> listar(@RequestParam(value = "page",required = false,defaultValue = "0") int page,@RequestParam(value = "size",required = false,defaultValue = "10") int size,@RequestParam(value = "sort",required = false) String sort,@RequestParam(value = "q",required = false) String q) {Pageable pageable = new PageableFactory(page, size, sort).getPageable();Page<Produto> resultPage;if (q == null) {resultPage = produtoRepository.findAll(pageable);} else {resultPage = produtoRepository.busca(q.toLowerCase(), pageable);}return resultPage;}Preciso criar um objeto Pageable para passar para o repositório, para isso criei uma fábrica para facilitar essa criação, a PageableFactory.
URL / api / produtos的示例性咨询:
{"content": [{"id": 2,"nome": "Processador Intel Core i7-9700K","valor": 2454},{"id": 3,"nome": "Headset Gamer HyperX Cloud Stinger - HX-HSCS-BK/NA ","valor": 189.37},{"id": 4,"nome": "Teclado Mecânico Gamer HyperX Mars, RGB, Switch Outemu Bluem, US - HX-KB3BL3-US/R4 ","valor": 284.11},{"id": 5,"nome": "Mouse Logitech M90 Preto 1000DPI ","valor": 26.9},{"id": 6,"nome": "Gabinete C3Tech Gamer ATX sem Fonte Preto MT-G50BK","valor": 119.6},{"id": 7,"nome": "Headphone Edifier Bluetooth W800BT Preto","valor": 250},{"id": 8,"nome": "Kindle Novo Paperwhite, 8GB, Wi-Fi, Preto - AO0705 ","valor": 418.99},{"id": 9,"nome": "SSD Kingston A400, 240GB, SATA, Leitura 500MB/s, Gravação 350MB/s - SA400S37/240G ","valor": 166},{"id": 10,"nome": "HD Seagate BarraCuda, 1TB, 3.5´, SATA - ST1000DM010","valor": 290},{"id": 11,"nome": "Cadeira Gamer DT3sports GT, Black - 10293-5","valor": 552.41}],"pageable": {"sort": {"unsorted": true,"sorted": false,"empty": true},"pageSize": 10,"pageNumber": 0,"offset": 0,"unpaged": false,"paged": true},"last": false,"totalPages": 2,"totalElements": 19,"numberOfElements": 10,"sort": {"unsorted": true,"sorted": false,"empty": true},"first": true,"size": 10,"number": 0,"empty": false
}
可能的外部建议
- http://127.0.0.1:8080/api/produtos?page=2
- http://127.0.0.1:8080/api/produtos?page=1&size=30
- http://127.0.0.1:8080/api/produtos?q=teclado
- http://127.0.0.1:8080/api/produtos?sort=valor,asc
- http://127.0.0.1:8080/api/produtos?sort=valor,desc
- http://127.0.0.1:8080/api/produtos?page=0&size=3&ort=valor,desc&q=intel
在端点列表上添加端点列表,然后在端点上添加一个示例:
@ApiOperation(value = "Busca um produto pelo id")@GetMapping(value = "/{id}")public ResponseEntity<Produto> listar(@PathVariable Integer id) {Optional<Produto> rastreador = produtoRepository.findById(id);if (!rastreador.isPresent()) {return ApiError.notFound("Produto não encontrado");}return new ResponseEntity<>(rastreador.get(), HttpStatus.OK);}
POST e PUT
Proparatoer ou atualizar um novo produto,vamos primeiramente criar uma classe ProdutoDTO.java。
O padrão DTO (Data transfer object) é uma classe que representa a entidade com apenas os atributos necessários para serem expostos publicamente, no nosso exemplo apenas preciso, na criação ou atualização, informar no nome e valor do produto, e nunca o seu id, por isso a sua classe de DTO não possui esse atributopublic class ProdutoDTO {private String nome;private Double valor;@ApiModelProperty(notes = "Nome do produto")public String getNome() {return nome;}public void setNome(String nome) {this.nome = nome;}public Double getValor() {return valor;}@ApiModelProperty(notes = "Valor do produto")public void setValor(Double valor) {this.valor = valor;}
}
Sendo assim,或其他代理人:
@ApiOperation(value = "Cria um novo Produto")@PostMapping()public ResponseEntity<Produto> criar(@RequestBody ProdutoDTO dto, UriComponentsBuilder ucBuilder) {try {//Crio um objeto da entidade preenchendo com os valores do DTO e validandoProduto produto = new Produto();if (dto.getNome() == null || dto.getNome().length() < 2) {return ApiError.badRequest("Informe o nome do produto");}produto.setNome(dto.getNome());if (dto.getValor() == null || dto.getValor() <= 0) {return ApiError.badRequest("Valor do produto inválido");}produto.setValor(dto.getValor());Produto novo = produtoRepository.save(produto);//Se ocorreu algum erro, retorno esse erro para a APIif (novo == null) {return ApiError.badRequest("Ocorreu algum erro na criação do produto");}//Se foi criado com sucesso, retorno o objeto criadoreturn new ResponseEntity<>(novo, HttpStatus.CREATED);} catch (Exception e) {LOGGER.error("Erro ao criar um produto", e);return ApiError.internalServerError("Ocorreu algum erro na criação do produto");}}
产品信息和徽标,徽标,密码,密码,示例,密码:
{
"nome": "Teclado Microsoft",
"valor": 124
}
Ainda nessemétodo,根据需要确认的面额,要求保留的权利,以及最终证明书或原始产品的永久证明。
没有最终的评论。 Esse processo pode ser visto abaixo。
产品的身份信息会引起混乱,并通知他人。
@ApiOperation(value = "Atualiza um Rastreador Equipamento")@PutMapping(value = "/{id}")public ResponseEntity<Produto> atualizar(@PathVariable("id") int id, @RequestBody ProdutoDTO dto) {try {Optional<Produto> produtoAtual = produtoRepository.findById(id);if (!produtoAtual.isPresent()) {return ApiError.notFound("Produto não encontrado");}if (dto.getNome() != null) {if (dto.getNome().length() < 2) {return ApiError.badRequest("Nome do produto inválido");}produtoAtual.get().setNome(dto.getNome());}if (dto.getValor() != null) {if (dto.getValor() <= 0) {return ApiError.badRequest("Valor do produto inválido");}produtoAtual.get().setValor(dto.getValor());}//Atualizo o objeto utilizando o repositórioProduto atualizado = produtoRepository.save(produtoAtual.get());//Se ocorreu algum erro, retorno esse erro para a APIif (atualizado == null) {return ApiError.internalServerError("Erro na atualização do produto");}//Se foi criado com sucesso, retorno o objeto atualizadoreturn new ResponseEntity<>(atualizado, HttpStatus.CREATED);} catch (Exception e) {LOGGER.error("Erro ao atualizar um produto", e);return ApiError.internalServerError("Erro na atualização do produto");}}
DELETE
一个简单的批注,简单,可验证的版本,一个可删除的版本库。
@ApiOperation(value = "Remove um produto")@DeleteMapping(value = "/{id}")public ResponseEntity<Produto> deletar(@PathVariable Integer id) {Optional<Produto> produto = produtoRepository.findById(id);if (!produto.isPresent()) {return ApiError.notFound("Produto não encontrado");} else {produtoRepository.deleteById(id);}return new ResponseEntity<>(HttpStatus.OK);}
Documentação com Swagger
O自动化,API作为动态DTO的类的控制对象。
请将SwaggerConfig com类作为必要的信息,以确保控制人员的需要。
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {@Beanpublic Docket api() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(apis()).paths(PathSelectors.any()).build();}private Predicate<RequestHandler> apis() {return RequestHandlerSelectors.basePackage("br.com.paulocollares.api.controladores.rest");}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("SPRING REST API").description("Documentação das APIs REST").contact(new Contact("pcollares", "www.paulocollares.com.br", null)).build();}@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}
}
Acessando URL /api/swagger-ui.html,podemos ver adocumentaçãogerada,como no exemplo abaixo。
No projeto há uma classe, MainController, que redireciona a requisição da raiz para essa página, ou seja, se acessar http://127.0.0.1:8080/api/ é redirecionado para http://127.0.0.1:8080/api/swagger-ui.html.
Testes
Para testar todos esses endpoins vamos usar o MockMVC para automatizar esse processo。 EleSerá负责提供发票和发票。
MockMVC的基础测试产品目录。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ProdutoController.class
})
public class ProdutoTest {//URL base para acesso desse controladorprivate final String BASE_URL = "/produtos";//Instância do ObjectMapper para trabalhar com JSONprivate ObjectMapper objectMapper;//Controlador REST tratado por meio de injeção de dependências@Autowiredprivate ProdutoController restController;//Instância do MockMVCprivate MockMvc mockMvc;//Instância do mock repository@MockBeanprivate ProdutoRepository mockRepository;@Beforepublic void setUp() {objectMapper = new ObjectMapper();mockMvc = MockMvcBuilders.standaloneSetup(restController).build();}...}
验证协议的最终目的是什么,验证协议的必要性,验证协议的有效性,验证请求的有效性 o终结点端点。
@Testpublic void buscar_id_200() throws Exception {Produto produto = new Produto();produto.setId(1);produto.setNome("Teste");produto.setValor(10.0);when(mockRepository.findById(1)).thenReturn(Optional.of(produto));mockMvc.perform(get(BASE_URL + "/1")).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk()).andExpect(jsonPath("$.id", is(1))).andExpect(jsonPath("$.nome", is("Teste"))).andExpect(jsonPath("$.valor", is(10.0)));verify(mockRepository, times(1)).findById(1);}
Essa mesmalógicaseráusada nos outros端点。
@Testpublic void buscar_id_404() throws Exception {mockMvc.perform(get(BASE_URL + "/2")).andExpect(status().isNotFound());}@Testpublic void criar_200() throws Exception {ProdutoDTO dto = new ProdutoDTO();dto.setNome("Teste");dto.setValor(11.0);Produto produto = new Produto();produto.setId(1);produto.setNome(dto.getNome());produto.setValor(dto.getValor());when(mockRepository.save(any(Produto.class))).thenReturn(produto);mockMvc.perform(post(BASE_URL).content(objectMapper.writeValueAsString(dto)).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)).andExpect(status().isCreated()).andExpect(jsonPath("$.id", is(1))).andExpect(jsonPath("$.nome", is("Teste"))).andExpect(jsonPath("$.valor", is(11.0)));verify(mockRepository, times(1)).save(any(Produto.class));}@Testpublic void atualizar_200() throws Exception {ProdutoDTO dto = new ProdutoDTO();dto.setNome("Teste");dto.setValor(11.0);Produto produto = new Produto();produto.setId(1);produto.setNome(dto.getNome());produto.setValor(dto.getValor());when(mockRepository.findById(1)).thenReturn(Optional.of(produto));when(mockRepository.save(any(Produto.class))).thenReturn(produto);mockMvc.perform(put(BASE_URL + "/1").content(objectMapper.writeValueAsString(dto)).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)).andExpect(status().isCreated()).andExpect(jsonPath("$.id", is(1)));}@Testpublic void deletar_200() throws Exception {Produto produto = new Produto();produto.setId(1);when(mockRepository.findById(1)).thenReturn(Optional.of(produto));mockMvc.perform(delete(BASE_URL + "/1")).andExpect(status().isOk());verify(mockRepository, times(1)).deleteById(1);}
Conclusão
Mostrei nesse post um simples exemplo de uma API REST com Spring, o código completo pode ser encontrado no Github: https://github.com/pcollares/api-rest-spring
Referências
Lista de links comreferênciaspara todos os assuntos abordados nesse post。
Conceitos
- https://restfulapi.net/resource-naming/
- https://blog.caelum.com.br/rest-principios-e-boas-praticas/
Spring
- https://spring.io/guides/gs/rest-service/
- https://blog.algaworks.com/como-criar-web-services-restful-com-spring-boot/
- https://www.mkyong.com/tutorials/spring-boot-tutorials/
- https://blog.algaworks.com/injecao-de-dependencias-com-spring/
Spring data JPA
- https://www.mkyong.com/spring-boot/spring-boot-spring-data-jpa-postgresql/
- https://www.mkyong.com/spring-boot/spring-boot-spring-data-jpa/
- https://domineospring.wordpress.com/2015/05/11/facilite-seus-daos-com-o-spring-data-jpa/
MockMVC
- http://marcelotozzi.com/teste/java/2014/09/10/usar-o-mockmvc-nos-testes-do-spring-e-mais-maneiro.html
- https://www.baeldung.com/integration-testing-in-spring
- https://howtodoinjava.com/spring-boot2/spring-boot-mockmvc-example/
- http://blog.marcnuri.com/mockmvc-spring-mvc-framework/
Swagger
- http://www.marcelferry.com.br/tutoriais/documentando-suas-api-com-swagger-usando-springfox/
- https://dzone.com/articles/swagger-generation-with-spring-boot
- https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
DTO
- https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application
- https://medium.com/@msealvial/blindando-sua-api-spring-boot-com-o-padr%C3%A3o-dto-44f97020d1a0
Paginação e ordenação
- https://dzone.com/articles/pagination-and-sorting-with-spring-data-jpa
[]的