Spring的第一步,Rest API,将前端与PUT结合在一起

关于自己的一些知识:目前,我是一名Skillbox学生,参加了“ Java开发人员”课程。我绝不谈广告。他于2019年5月开始学习Java,之后他自己单独学习了HTML,CSS和JS。

实际上,这促使我写这篇文章时意识到前端如何与后端一起工作,并且对PUT请求缺乏了解。在我“ google”任何地方的Rest API都是通过POST和GET请求实现的,有时是使用DELETE实现的,没有前端的示例。首先,我想传达的是与我将REST API和前端一起实现的方式一样,以便能够理解。但是这篇文章不仅适合我本人,也适合Spring技术的有经验的用户,因为在评论中,我希望看到老同志的正当教导。毕竟,我将根据我的经验描述我的决定(请阅读经验不足的内容)。

我遇到了理解Spring的问题,特别是对PUT请求的理解,即更改数据库中元素的数据。我还将描述POST和GET请求。通常,标准CRUD(如果我输入错了,则正确)。还有一点前端,也就是说,在那里,一个请求被发送到服务器并处理了响应。

我用了:

  • 马文
  • 的MySQL
  • IntelliJ IDEA

我还想保留前端部分在培训项目中的功能,我实现了PUT和DELETE查询。

可以在GitHub上查看整个项目

一个小技巧:使用maven创建项目时,Java版本跳至第五个,要在pom.xml中修复此问题,我们编写以下代码,其中数字为版本。

开放生活黑客
<properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
</properties>


弹簧连接


首先,在pom.xml中,我们将Spring boot作为父级连接,这是因为依赖关系的进一步连接不会因版本而冲突:

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

现在,我们连接Spring Web负责启动应用程序:

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

我们创建并运行该应用程序


您需要在正确的目录src / main / java / main中开始编写应用程序,是的,没错,我还没有找到解释性的解释,我想我会慢慢发现的。

并立即布置应用程序的整个结构。

图片

我所做的第一件事是创建Main.java类以运行带有注释的应用程序@SpringBootApplication

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

而且您已经可以单击运行,甚至我的服务器都将启动!

图片

在端口8080上启动。您可以转到该地址http://localhost:8080/,我们将看到错误404,因为还没有页面。

公平地说,您需要实现一个前端。

您需要在pom.xml中包括依赖关系以模板化HTML。

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

首先,请访问src / main / resources / templates目录中index.html起始页

这里有这样简单的标记
<!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>


我们还在src / main / resources / static / css目录中编写样式,创建styles.css

查看样式
* {
    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;
}


您可以尝试启动该应用程序然后继续http://localhost:8080/,您可以欣赏起始页,尽管到目前为止没有任何操作。

当然,src / main / resources / static / js目录中的带有jQuery插件的js,我再次预约,在培训项目中已经有jQuery和main.js的一部分
所有关于Java和Spring同一集线器,所以链接的完整代码的js认为将是足够的:
链接到jQuery的3.4.0.min.js
链接到main.js

以下将特别注意GET和PUT的请求。无论是从服务器端还是从前端。

现在,您可以尝试启动项目,并确保前端也可以正常工作并执行操作(添加按钮将启动表单)。

数据库交互


下一步是与数据库进行交互的实体,为此,我们连接了Spring数据jpa依赖项:

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

然后在src / main / java / main / model目录中创建一个POJO类Todo附加注释@Entity

我们声明字段,我将拥有:ID,名称,描述,日期。

单独注意setDate(),我只是在String的输入处执行了该操作,然后转换为java.util.Date,甚至使用atStartOfDay().atZone(ZoneId.of("UTC"),我也注意了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());
    }
}

pom.xml中添加依赖项以建立与MySQL的连接:

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

src / main / resources目录中,创建application.properties并写入数据以连接到数据库:

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

现在让我们继续创建存储库。在src / main / java / main / model目录中,创建带有注释的TodoRepository接口,@Repository并继承CrudRepository <Todo,Integer>。抒情离题-据我了解,这是数据库和控制器之间的垫圈,Spring很有用,您无需创建套接字,无需担心与数据库交互,它可以为您做所有事情。

package main.model;

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

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

实际上,与数据库的通信将通过此存储库进行。

现在是时候创建一个控制器,在该控制器中处理来自前端的请求,与数据库的交互以及前端响应。

src / main / java / main / controller目录中,创建带有注释的TodoController类@RestController,声明TodoRepository变量并通过构造函数进行初始化。

让我们从POST请求开始。我们创建add()方法来接受Todo并返回int(id),并用批注标记@PostMapping(“/todo-list/”)和添加路径。我们获取存储库,save()并将请求随附的Todo对象保存到方法中的数据库中。只是魔术。

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

通常,类似于GET和DELETE,但使用id并在ResponseEntity shell中返回Todo。还要注意,该方法的参数get()带有注释,下面有更详细的说明。然后生成一个响应ResponseEntity.ok(todoOptional.get());,即代码200,或者如果此id找不到该响应,则返回主体为null的代码404。

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

前端方面发生了什么?

使用GET示例:

单击todo =>列表中的链接,我们拉出id todo =>形成一个请求(请注意,id本身未传递给该方法。get()方法中的id是从(value =“ / todo-list / {id} “)为此,我们需要@PathVariable在方法参数中添加一个注释)=>答案以Todo对象的形式出现=>前端会执行其认为合适的操作,在这种情况下,Todo具有说明和日期。

代码段main.js,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;
});


让我们创建另一个控制器,该控制器将立即在开始页面上显示待办事项列表。我们还使用存储库并获取Todo列表,然后将todoList神奇地传递给前端:

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

在index.html中进行了此类更正后,动态加载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>

提出要求


在TodoController中,我们创建一个方法put(),该方法@PutMapping在输入Map <String,String>上带有注释,@RequestParam带有注释和int,该int从值提取,位于封装在ResponseEntity中的Todo的输出。而且该存储库没有update()方法,因此所有操作如下:

Todo是通过todoRepository通过id =>从新数据库中提取的Todo =>分配了新参数Todo =>它通过该存储库保存在数据库中=>发送前端响应

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

此时,在前端:

单击“更改”按钮=>从元素中收集Todo数据=>编辑表单以更改大小写(重命名表单名称,并将按钮替换为输入值Todo data)=>表单已打开=>驱动更改数据=>单击以下形式的“更改”按钮=>数据收集=>生成了一个PUT请求(路径,数据)=>接收到来自修改的Todo对象的响应,但是具有相同的id =>前端执行了它想要的操作,在这种情况下是数据替换去做。

代码段main.js,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;
    });
});


您可以在github上更详细地了解该项目

它是为初学者而设计的,但是我想听听经验丰富的用户的建设性批评。如果您在注释中解释为什么在请求PUT时为什么Map <String,String>出现在控制器端,为什么我不能在那里提交Todo,也很酷。

资源:


All Articles