Os primeiros passos do Spring, Rest API, concentram-se em PUT em conjunto com o frontend

Um pouco sobre mim: No momento, sou aluno do Skillbox e faço o curso “Java-developer”. Em nenhum caso, não em publicidade, falo um pouco de mim. Ele começou a aprender Java em maio de 2019, antes de estudar sozinho HTML, CSS e JS.

Na verdade, ele me levou a escrever este artigo com uma consciência de como o frontend trabalha com o back-end juntos e com uma falta de entendimento da solicitação PUT. Em todos os lugares onde eu "google" a API Rest foi implementada com solicitações POST e GET, às vezes com DELETE e não havia exemplos do frontend. Gostaria de transmitir, antes de tudo, o mesmo que implemento a API REST junto com o frontend, para que o entendimento chegue. Mas o artigo destina-se não apenas aos iniciantes que eu sou, mas também aos usuários experientes das tecnologias Spring, porque nos comentários quero ver as instruções corretas dos camaradas seniores. Afinal, descreverei minha decisão com base em minha experiência (leia a falta de experiência).

Eu me deparei com o problema de entender o Spring e, especificamente, com a solicitação PUT, ou seja, alterar os dados de um elemento no banco de dados. Também descreverei solicitações POST e GET. Em geral, CRUD padrão (corrija se estiver errado). E também um pequeno frontend, ou seja, como lá, uma solicitação é enviada ao servidor e a resposta é processada.

Eu usei:

  • Maven
  • MySQL
  • IntelliJ IDEA

Também quero fazer uma reserva de que o frontend estava parcialmente no projeto de treinamento, implementei as consultas PUT e DELETE.

Todo o projeto pode ser visualizado no GitHub .

Um pequeno hack ao vivo: Ao criar um projeto com o maven, a versão java salta para a quinta, para corrigir isso no pom.xml, escrevemos o seguinte, onde o número é a versão.

Open Life Hack
<properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
</properties>


Conexão por mola


Para começar, no pom.xml conectamos a inicialização Spring como pai, isso é explicado pelo fato de que outras conexões de dependências não entram em conflito por versão:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.4.RELEASE</version>
</parent>

Agora, nos conectamos, o Spring web é responsável por iniciar o aplicativo:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Criamos e executamos o aplicativo


Você precisa começar a escrever o aplicativo no diretório correto, ou seja, src / main / java / main , sim, isso mesmo, ainda não encontrei uma explicação explicativa para isso, acho que vou descobrir com o tempo.

Esqueça imediatamente toda a estrutura do aplicativo.

imagem

A primeira coisa que fiz foi criar uma classe Main.java para executar o aplicativo com anotação @SpringBootApplication:

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

E você já pode clicar em executar e até o Meu servidor será iniciado!

imagem

Lançado na porta 8080. Você pode ir para o endereço http://localhost:8080/e veremos o erro 404 porque ainda não há páginas.

Para ser justo, você precisa implementar um frontend.

Você precisa incluir a dependência no pom.xml no modelo HTML.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Para começar, a página inicial index.html no diretório src / main / resources / templates .

Aqui com essa marcação simples
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ToDo List</title>
    <script src="/js/jquery-3.4.0.min.js"></script>
    <script src="/js/main.js"></script>
    <link rel="stylesheet" type="text/css" href="/css/styles.css">
</head>
<body>
    <div id="todo-form">
        <form>
            <label> :
            </label>
            <input id="todo-form-name" type="text" name="name" value="">
            <label>:
            </label>
            <input id="todo-form-description" type="text" name="description" value="">
            <label>  :
            </label>
            <input id="todo-form-date" type="date" name="date" value="">
            <hr>
        </form>
    </div>
    <h1> </h1>
    <button id="show-add-todo-list"> </button>
    <br><br>
    <div id="todo-list">

    </div>
</body>
</html>


Também escrevemos estilos no diretório src / main / resources / static / css, criamos styles.css

Ver estilos
* {
    font-family: Arial, serif;
}

#todo-form {
    display: none;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 10;
    background-color: #88888878;
}

#todo-form form {
    background-color: white;
    border: 1px solid #333;
    width: 300px;
    padding: 20px;
}

#todo-form h2 {
    margin-top: 0;
}

#todo-form label {
    display: block;
}

#todo-form form > * {
    margin-bottom: 5px;
}

h4 {
    margin-bottom: 0;
}


Você pode tentar iniciar o aplicativo e prosseguir http://localhost:8080/e pode admirar a página inicial, embora até agora sem uma ação.

E, é claro, js com o plugin jQuery no diretório src / main / resources / static / js , mais uma vez farei uma reserva, já havia jQuery e uma parte do main.js redigida no projeto de treinamento .
Tudo o mesmo hub sobre Java e Spring, para que links para os js código completo acha que vai ser o suficiente:
Link para jquery-3.4.0.min.js
Link para main.js .

Abaixo, será dada uma atenção especial à solicitação de GET e PUT. Tanto do lado do servidor quanto do front-end.

Agora você pode tentar iniciar o projeto e garantir que o frontend funcione e a ação também (o botão Adicionar inicia o formulário).

Interação com o banco de dados


O próximo passo é a entidade para interagir com o banco de dados. Para isso, conectamos a dependência jpa de dados do Spring:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

E no diretório src / main / java / main / model eu crio uma classe POJO Todo, anexo uma anotação @Entity.

Declaramos campos, e terei: id, nome, descrição, data.

Atenção especial setDate(), fiz exatamente isso, na entrada de String e depois na conversão para java.util.Date, e mesmo com atStartOfDay().atZone(ZoneId.of("UTC"), também prestei atenção na anotação do campo date:

package main.model;

import javax.persistence.*;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

@Entity
public class Todo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    private String description;
    @Temporal(TemporalType.DATE)
    private Date date;

    //getters and setters …
   
    public void setDate(String date) {
        this.date = Date.from(LocalDate.parse(date).atStartOfDay().atZone(ZoneId.of("UTC")).toInstant());
    }
}

Adicione uma dependência no pom.xml para estabelecer uma conexão com o MySQL:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>

No diretório src / main / resources , crie application.properties e grave dados para conectar-se ao banco de dados:

spring.datasource.url=jdbc:mysql://localhost:3306/todolist?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=none

Agora vamos continuar a criar o repositório. No diretório src / main / java / main / model , crie a interface TodoRepository com anotação @Repositorye herde CrudRepository <Todo, Inteiro>. Digressão lírica - como eu entendo, isso é uma disposição entre o banco de dados e o controlador, o Spring é bom nisso, você não precisa criar soquetes, não precisa se preocupar em interagir com o banco de dados, ele faz tudo por você.

package main.model;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TodoRepository extends CrudRepository<Todo, Integer> {
}

Na verdade, a comunicação com o banco de dados ocorrerá através deste repositório.

Agora é hora de criar um controlador no qual as solicitações do front-end, a interação com o banco de dados e as respostas do front-end serão processadas.

No diretório src / main / java / main / controller , crie a classe TodoController com anotação @RestController, declare a variável TodoRepository e inicialize através do construtor.

Vamos começar com a solicitação POST. Criamos o método add () que aceita Todo e retorna int (id), marca com anotação @PostMapping(“/todo-list/”)e o caminho onde adicionaremos. Pegamos o repositório e save()salvamos o objeto Todo que acompanha a solicitação no banco de dados no método Apenas mágica.

@PostMapping("/todo-list/")
public int add(Todo todo) {
    Todo newTodo = todoRepository.save(todo);
    return newTodo.getId();
}

Em geral, semelhante a GET e DELETE, mas usando id e retornando Todo no shell ResponseEntity. Observe também que o parâmetro do método está get()marcado com uma anotação, abaixo é um pouco mais detalhado. Em seguida, é gerada uma resposta ResponseEntity.ok(todoOptional.get());, ou seja, o código 200 ou, se não for encontrado por esse ID, retorna o código 404 com o corpo nulo.

@GetMapping("/todo-list/{id}")
public ResponseEntity<Todo> get(@PathVariable int id){
    Optional<Todo> todoOptional = todoRepository.findById(id);
    if(todoOptional.isEmpty()){
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
    }
    return ResponseEntity.ok(todoOptional.get());
}

O que está acontecendo no lado da interface?

Usando o exemplo GET:

clicando em um link na lista todo =>, retiramos a identificação todo => uma solicitação é formada (observe que o próprio ID não é passado para o método. O ID no método get () é extraído de (value = "/ todo-list / {id} ") para isso, precisamos de uma anotação @PathVariableno parâmetro method) => a resposta vem na forma de um objeto Todo => o front-end faz o que bem entender; nesse caso, Todo tem uma descrição e uma data.

Parte do código main.js, implementação GET
$(document).on('click', '.todo-link', function(){
    var link = $(this);
    var todoId = link.data('id');
    $.ajax({
        method: "GET",
        url: '/todo-list/' + todoId,
        success: function(response)
        {
            if($('.todo-div > span').is('#' + todoId)){
                return;
            }
            link.parent().append(codeDataTodo(response, todoId));
        },
        error: function(response)
        {
            if(response.status == 404) {
                alert('  !');
            }
        }
    });
    return false;
});


Vamos criar outro controlador que exibirá imediatamente a lista de tarefas na página inicial. Também trabalhamos com o repositório e obtemos a lista Todo e, em seguida, todoList é passado magicamente para o front-end:

@Controller
public class DefaultController {
    @Autowired
    TodoRepository todoRepository;
    @RequestMapping("/")
    public String index(Model model){
        Iterable<Todo>  todoIterable = todoRepository.findAll();
        ArrayList<Todo> todoList = new ArrayList<>();
        for(Todo todo : todoIterable){
            todoList.add(todo);
        }
        model.addAttribute("todoList", todoList);
        return "index";
    }
}

Aqui, com essas correções em index.html, ocorre o carregamento dinâmico de todoList:

<html lang="en" xmlns:th="http://www.thymeleaf.org">

<div id="todo-list">
    <div class="todo-div" th:each="todo : ${todoList}" th:attr="id=${todo.id}">
        <a href="#" class="todo-link" th:attr="data-id=${todo.id}" th:text="${todo.name}"></a>
        <br>
    </div>
</div>

Solicitação PUT


Em TodoController, criamos um método put()com uma anotação @PutMappingna entrada Map <String, String> com uma anotação @RequestParame um int, extraído do valor, na saída de Todo, envolvida em ResponseEntity. E também o repositório não possui o método update (), então tudo acontece da seguinte forma:

Todo é extraído do banco de dados via todoRepository por id => novos parâmetros são atribuídos Todo => é salvo no banco de dados através do repositório => a resposta do front-end é enviada

@PutMapping(value = "todo-list/{id}")
public ResponseEntity<Todo> put(@RequestParam Map<String, String> mapParam, @PathVariable int id){
    Optional<Todo> todoOptional = todoRepository.findById(id);
    if(todoOptional.isEmpty()){
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
    }
    todoOptional.get().setName(mapParam.get("name"));
    todoOptional.get().setDescription(mapParam.get("description"));
    todoOptional.get().setDate(mapParam.get("date"));
    todoRepository.save(todoOptional.get());
    return ResponseEntity.ok(todoOptional.get());
}

No front-end, neste momento:

clique no botão "Alterar" => Todos os dados são coletados do elemento => o formulário é editado para alterar o caso (o nome do formulário é renomeado e o botão é substituído no valor de entrada Todo data) => o formulário é aberto => os dados para alteração são conduzidos => clique no botão "Alterar" no formulário => coleta de dados => uma solicitação PUT é gerada (caminho, dados) => recebendo uma resposta do objeto Todo modificado, mas com o mesmo id => o front-end faz o que deseja, neste caso substituição de dados Façam.

Parte do código main.js, implementação de PUT
//Update _todo and show updating _todo form
$(document).on('click', '#show-update-todo-list', function(){
    var buttonUpdate = $(this);
    var todoId = buttonUpdate.data('id');
    var todoName = buttonUpdate.data('name');
    var todoDescription = buttonUpdate.data('description');
    var todoDate = buttonUpdate.data('date');
    todoFormNameAndButton(' ', '', 'update-todo');
    todoInputValue(todoName, todoDescription, todoDate);
    $('#todo-form').css('display', 'flex');
    $('#update-todo').click(function() {
        var data = $('#todo-form form').serialize();
        $.ajax({
            method: "PUT",
            url: '/todo-list/' + todoId,
            data: data,
            success: function(response) {
                $('#todo-form').css('display', 'none');
                response.date = response.date.slice(0,10);
                $('.todo-div#' + todoId  + ' > a').text(response.name);
                $('.todo-div#' + todoId +' > span').replaceWith(codeDataTodo(response, todoId));
            }
        });
        return false;
    });
});


Você pode se familiarizar com o projeto em mais detalhes no github .

Ele foi escrito para iniciantes, mas gostaria de ouvir críticas construtivas de usuários experientes. É muito legal se você explicar nos comentários por que o Map <String, String> vem do lado do controlador ao solicitar PUT, por que não consigo enviar o Todo lá.

Recursos:


All Articles