Die ersten Schritte in Spring, Rest API, konzentrieren sich auf PUT in Verbindung mit dem Frontend

Ein wenig über mich selbst: Im Moment bin ich ein Skillbox-Student und nehme am Kurs „Java-Entwickler“ teil. Auf keinen Fall Werbung, ich erzähle ein wenig über mich. Er begann im Mai 2019 Java zu lernen, bevor er selbst ein wenig HTML, CSS und JS studierte.

Eigentlich hat es mich dazu gebracht, diesen Artikel mit dem Bewusstsein zu schreiben, wie das Frontend mit dem Backend zusammenarbeitet, und mit einem Unverständnis für die PUT-Anfrage. Überall, wo ich google, wurde die Rest-API mit POST- und GET-Anforderungen implementiert, manchmal mit DELETE, und es gab keine Beispiele für das Frontend. Ich möchte zunächst das Gleiche vermitteln, wie ich die REST-API zusammen mit dem Frontend implementiere, damit das Verständnis kommt. Der Artikel richtet sich aber nicht nur an Anfänger, die ich bin, sondern auch an erfahrene Anwender von Spring-Technologien, denn in den Kommentaren möchte ich die rechtschaffenen Lehren älterer Genossen sehen. Schließlich werde ich meine Entscheidung anhand meiner Erfahrung beschreiben (lesen Sie den Mangel an Erfahrung).

Ich bin auf das Problem gestoßen, Spring zu verstehen, und zwar speziell mit der PUT-Anforderung, dh dem Ändern der Daten eines Elements in der Datenbank. Ich werde auch POST- und GET-Anfragen beschreiben. Im Allgemeinen Standard-CRUD (richtig, wenn ich falsch liege). Und auch ein kleines Frontend, das heißt, da wird eine Anfrage an den Server gesendet und die Antwort verarbeitet.

Ich benutzte:

  • Maven
  • MySQL
  • IntelliJ IDEE

Ich möchte auch reservieren, dass das Frontend teilweise im Schulungsprojekt enthalten war. Ich habe PUT- und DELETE-Abfragen implementiert.

Das gesamte Projekt kann auf GitHub angesehen werden .

Ein kleiner Live-Hack: Beim Erstellen eines Projekts mit maven springt die Java-Version zum fünften. Um dies in pom.xml zu beheben, schreiben wir Folgendes, wobei die Nummer die Version ist.

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


Federverbindung


Für den Anfang, in pom.xml verbinden wir Spring Boot als übergeordnetes Element. Dies wird durch die Tatsache erklärt, dass die weitere Verbindung von Abhängigkeiten nicht nach Version in Konflikt steht:

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

Jetzt verbinden wir Spring Web ist verantwortlich für den Start der Anwendung:

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

Wir erstellen und führen die Anwendung aus


Sie müssen damit beginnen, die Anwendung in das richtige Verzeichnis zu schreiben, nämlich src / main / java / main . Ja, das ist richtig. Ich habe noch keine erklärende Erklärung dafür gefunden. Ich denke, ich werde es mit der Zeit herausfinden.

Und legen Sie sofort die gesamte Struktur der Anwendung fest.

Bild

Als erstes habe ich eine Main.java- Klasse erstellt, um die Anwendung mit Anmerkungen auszuführen @SpringBootApplication:

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

Und Sie können bereits auf Ausführen klicken und sogar Mein Server wird gestartet!

Bild

Wird auf Port 8080 gestartet. Sie können zur Adresse gehen, http://localhost:8080/und es wird Fehler 404 angezeigt, da noch keine Seiten vorhanden sind.

Fairerweise müssen Sie ein Frontend implementieren.

Sie müssen die Abhängigkeit in pom.xml zum HTML-Template einfügen.

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

Zunächst die Startseite index.html im Verzeichnis src / main / resources / templates .

Hier mit so einfachem Aufschlag
<!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>


Wir schreiben auch Stile in das Verzeichnis src / main / resources / static / css und erstellen die Datei styles.css

Stile anzeigen
* {
    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;
}


Sie können versuchen, die Anwendung zu starten und loszulegen, http://localhost:8080/und Sie können die Startseite bewundern, allerdings bisher ohne Aktion.

Und natürlich js mit dem jQuery-Plugin im Verzeichnis src / main / resources / static / js. Ich werde noch einmal eine Reservierung vornehmen. Es gab bereits jQuery und einen Teil von main.js, die im Schulungsprojekt geschrieben wurden .
Alle der gleiche Hub über Java und Spring, so Links zu dem vollständigen Code js denken genug:
Link zu jquery-3.4.0.min.js
Link zu main.js .

Im Folgenden wird ein besonderes Augenmerk auf die Anfrage von GET und PUT gelegt. Sowohl von der Serverseite als auch vom Frontend.

Jetzt können Sie versuchen, das Projekt zu starten und sicherstellen, dass das Frontend und auch die Aktion funktionieren (die Schaltfläche Hinzufügen startet das Formular).

DB-Interaktion


Der nächste Schritt ist die Entität für die Interaktion mit der Datenbank. Dazu verbinden wir die Spring-Daten-JPA-Abhängigkeit:

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

Und im Verzeichnis src / main / java / main / model erstelle ich eine POJO-Klasse, um eine Anmerkung anzuhängen @Entity.

Wir deklarieren Felder, ich werde haben: ID, Name, Beschreibung, Datum.

Besondere Aufmerksamkeit setDate(), genau das habe ich bei der Eingabe von String und der anschließenden Konvertierung in java.util.Date getan , und auch bei habe atStartOfDay().atZone(ZoneId.of("UTC")ich auch auf die Anmerkung des Datumsfeldes geachtet:

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

Fügen Sie eine Abhängigkeit in pom.xml hinzu , um eine Verbindung zu MySQL herzustellen:

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

Erstellen Sie im Verzeichnis src / main / resources application.properties und schreiben Sie Daten, um eine Verbindung zur Datenbank herzustellen:

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

Fahren wir nun mit der Erstellung des Repositorys fort. Im src / main / java / main / Modell - Verzeichnis, erstellen Sie die TodoRepository Schnittstelle mit Annotation @Repositoryund vererben CrudRepository <Todo, Integer>. Lyrischer Exkurs - so wie ich es verstehe, ist dies eine Dichtung zwischen der Datenbank und dem Controller. Spring ist gut darin, Sie müssen keine Sockets erstellen, Sie müssen sich keine Sorgen um die Interaktion mit der Datenbank machen, es erledigt alles für Sie.

package main.model;

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

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

Tatsächlich erfolgt die Kommunikation mit der Datenbank über dieses Repository.

Jetzt ist es Zeit, einen Controller zu erstellen, in dem Anforderungen vom Front-End, die Interaktion mit der Datenbank und Front-End-Antworten verarbeitet werden.

Im src / main / java / main / Controller - Verzeichnis, erstellen Sie die TodoController Klasse mit Anmerkung @RestController, erklären die TodoRepository Variable und initialisiert durch den Konstruktor.

Beginnen wir mit der POST-Anfrage. Wir erstellen die add () -Methode, die Todo akzeptiert und int (id) zurückgibt, markieren sie mit Anmerkungen @PostMapping(“/todo-list/”)und dem Pfad, den wir hinzufügen werden. Wir nehmen das Repository und save()speichern das Todo-Objekt, das mit der Anforderung geliefert wurde, in der Datenbank in der Methode . Nur Magie.

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

Im Allgemeinen ähnlich wie GET und DELETE, jedoch unter Verwendung von id und Rückgabe von Todo in der ResponseEntity-Shell. Beachten Sie auch, dass der Parameter der Methode get()mit einer Anmerkung gekennzeichnet ist. Im Folgenden wird etwas ausführlicher darauf eingegangen. Dann wird eine Antwort generiert ResponseEntity.ok(todoOptional.get());, dh Code 200, oder wenn sie von dieser ID nicht gefunden wird, wird Code 404 mit body null zurückgegeben.

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

Was passiert auf der Frontend-Seite?

Anhand des GET-Beispiels:

Wenn Sie auf einen Link in der Liste todo => klicken, wird die ID todo => angezeigt. Es wird eine Anforderung gebildet (beachten Sie, dass die ID selbst nicht an die Methode übergeben wird. Die ID in der Methode get () wird aus (value = "/ todo-list / {id} extrahiert) ") Dazu benötigen wir eine Annotation @PathVariableim Methodenparameter) => Die Antwort kommt in Form eines Todo-Objekts => Das Frontend macht das, was es für richtig hält. In diesem Fall hat Todo eine Beschreibung und ein Datum.

Stück Code main.js, GET-Implementierung
$(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;
});


Erstellen wir einen weiteren Controller, der sofort die Aufgabenliste auf der Startseite anzeigt. Wir arbeiten auch mit dem Repository und erhalten die Todo-Liste, und dann wird die Aufgabenliste auf magische Weise an das Front-End übergeben:

@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";
    }
}

Hier tritt bei solchen Korrekturen in index.html das dynamische Laden von todoList auf:

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

PUT-Anfrage


In TodoController erstellen wir eine Methode put()mit einer Anmerkung @PutMappingan der Eingabe Map <String, String> mit einer Anmerkung @RequestParamund einem int, das aus dem Wert extrahiert wird und in ResponseEntity am Ausgang von Todo eingeschlossen ist. Außerdem verfügt das Repository nicht über die update () -Methode, sodass alles wie folgt

abläuft : Todo wird über todoRepository aus der Datenbank extrahiert, indem id => neue Parameter zugewiesen werden Todo => es wird über das Repository in der Datenbank gespeichert => die Frontend-Antwort wird gesendet

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

Zu diesem Zeitpunkt

am Frontend: Klicken Sie auf die Schaltfläche „Ändern“ => Todo-Daten werden vom Element erfasst => Das Formular wird zum Ändern des Falls bearbeitet (der Name des Formulars wird umbenannt und die Schaltfläche wird durch den Eingabewert Todo-Daten ersetzt) ​​=> Das Formular wird geöffnet => Die Daten zum Ändern werden eingegeben => Klicken Sie im Formular auf die Schaltfläche „Ändern“ => Datenerfassung => Es wird eine PUT-Anforderung generiert (Pfad, Daten) => Eine Antwort vom geänderten Todo-Objekt wird empfangen, aber mit derselben ID => macht das Frontend, was es will, in diesem Fall Daten ersetzen Machen.

Stück Code main.js, Implementierung von 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;
    });
});


Sie können sich auf dem Github detaillierter mit dem Projekt vertraut machen .

Es ist für Anfänger von Anfängern geschrieben, aber ich würde gerne konstruktive Kritik von erfahrenen Benutzern hören. Es ist genauso cool, wenn Sie in den Kommentaren erklären, warum Map <String, String> auf der Controllerseite steht, wenn Sie nach PUT fragen, warum ich Todo dort nicht einreichen kann.

Ressourcen:


All Articles