Spring MVC learning summary RESTful introduction / HiddenHttpMethodFilter filter source code analysis / sending put and delete requests using RESTful style / complete cases of RESTful style

Keywords: RESTful

1, Introduction to RESTful

REST: Representational State Transfer, the state transfer of presentation layer resources.

  • resources:
    Resource is a way of looking at the server, that is, the server is regarded as composed of many discrete resources. Each resource is a named abstraction on the server. Because resource is an abstract concept, it can not only represent a file in the server file system, a table in the database and other specific things, but also design resources as abstract as possible, as long as imagination allows and client application developers can understand. Similar to object-oriented design, resources are organized with nouns as the core, and nouns are the first concern. A resource can be identified by one or more URIs. A URI is not only the name of a resource, but also the address of the resource on the Web. Client applications interested in a resource can interact with it through the URI of the resource.
  • Description of resources:
    The description of resources is a description of the state of resources at a specific time. It can be transferred (exchanged) between client and server. The expression of resources can be in many formats, such as HTML/XML/JSON / plain text / picture / video / audio, etc. The expression format of resources can be determined through negotiation mechanism. The request response direction is usually expressed in different formats.
  • State transition:
    State transfer refers to the expression that transfer represents the state of resources between the client and the server. Through the expression of transfer and operation resources, the purpose of operating resources can be realized indirectly.

2, RESTful implementation

Specifically, there are four verbs representing the operation mode in the HTTP protocol: GET, POST, PUT and DELETE. They correspond to four basic operations: GET is used to obtain resources, POST is used to create new resources, PUT is used to update resources, and DELETE is used to DELETE resources.

REST style advocates the uniform style design of URL address. Each word from front to back is separated by slash. The request parameters are not carried by question mark key value pair, but the data to be sent to the server is taken as a part of the URL address to ensure the consistency of the overall style.

operationTraditional wayREST style
Query operationgetUserById?id=1user/1 – > get request method
Save operationsaveUseruser – > post request mode
Delete operationdeleteUser?id=1user/1 – > delete request method
update operationupdateUseruser – > put request mode

Generally speaking, querying, saving, deleting and updating user are all operations on the user resource, so these four operations can be written to user in the resource layer of the URL, and the specific different operations are determined by the request method.

(1) Using RESTful style to implement get and post requests

first, Create a web module, configure web.xml and deploy Tomcat

controller:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MyController {

    @RequestMapping(value = "/")
    public String toIndex(){
        return "index";
    }

    /*
    * Use RESTFul to simulate the addition, deletion, modification and query of user resources:
     * /user    GET    Query all user information
    * /user/1   GET    Query user information according to user id
    * /user     POST   Add user information
    */
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String selectUsers(){
        System.out.println("Query all user information");
        return "target";
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String selectUserById(){
        System.out.println("According to user id Query user information");
        return "target";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String addUser(){
        System.out.println("Add user information");
        return "target";
    }
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>home page</title>
</head>
<body>
This is index.html<br>
<a th:href="@{/user}">Query all user information</a><br>
<a th:href="@{/user/1}">according to id Query user information</a><br>
<form th:action="@{/user}" method="post">
    user name:<input type="text" name="username"><br>
    password:<input type="password" name="password"><br>
    <input type="submit" value="Add user information"><br>
</form>
</body>
</html>

target.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
This is target.html<br>
</body>
</html>

Run Tomcat:

Click the first link:

IDEA output:

Click the second link:

IDEA output:

Fill out the form:


IDEA output:

(2) Use RESTful style to implement put and delete requests

1.HiddenHttpMethodFilter

Since the browser only supports sending get and post requests, even if you write "put" or "delete" in the method of the form form, it will be regarded as a get request. How do you send put and delete requests?

Spring MVC provides HiddenHttpMethodFilter to help us convert POST requests into DELETE or PUT requests.

Let's take a look at the source code of HiddenHttpMethodFilter:

The following is the actual filtering method of HiddenHttpMethodFilter:

It will first intercept our request and assign the request object to requestToUse. If "POST".equals(request.getMethod()) & & request. Getattribute ("javax. Servlet. Error. Exception") = = null is true, the following code will be executed. The latter part is true without entanglement. We just need to take care of the previous "POST".equals(request.getMethod()), Therefore, to send a put or delete request, we need to set the request to post. Next, string paramValue = request. Getparameter (this. Methodparameter);, this.methodParam is shown in the previous figure_ Method ", that is, the request object is named"_ The parameter value of "method" is passed to paramValue. If you want to execute the following code, you must meet StringUtils.hasLength(paramValue), which means that if paramValue has a length, execute it down. So if we want to send a put or delete request, one more thing to do is to set a name in the request called "_ Method "and assign a value to it. Next, String method = paramValue.toUpperCase(Locale.ENGLISH); This means that all the values of this paramValue are capitalized and passed to the string object method, and then ALLOWED_METHODS.contains(method), if allowed_ The methods collection contains the string represented by method before execution. Let's take a look at ALLOWED_METHODS:

It contains the names of three requests: PUT, DELETE and PATCH. If yes, requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method); A new request object will be created and re assigned to requestToUse. Let's look at the source code of this HttpMethodRequestWrapper class:

It will create a request object that encapsulates the incoming request method. Finally, filterChain.doFilter((ServletRequest)requestToUse, response); fileterChain releases our request object so that we can access the target resource. So far, we have realized sending put or delete requests.

Summary:
HiddenHttpMethodFilter to process put and delete requests:

  1. The request mode of the current request must be post
  2. The current request must transmit the request parameters_ method, the parameter value is put or delete

If the above conditions are met, the HiddenHttpMethodFilter filter will convert the request mode of the current request into request parameters_ Method, so the request parameter_ The value of method is the final request method.

2. Send put request

Case: send put request
Register HiddenHttpMethodFilter in web.xml:

    <!--Register filter HiddenHttpMethodFilter-->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

controller:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MyController {
    //Visit the home page
    @RequestMapping(value = "/")
    public String toIndex(){
        return "index";
    }

    //  /User put modify user information
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String updateUser(){
        System.out.println("Modify user information");
        return "target";
    }
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>home page</title>
</head>
<body>
This is index.html<br>
<form th:action="@{/user}" method="post">
    <input type="hidden" name="_method" value="put"><!--Set parameters_method The value of is put. Users do not need to know this, so hidden-->
    user name:<input type="text" name="username"><br>
    password:<input type="password" name="password"><br>
    <input type="submit" value="Modify user information"><br>
</form>
</body>
</html>

Start Tomcat:


IDEA output:

Note: so far, spring MVC has provided two filters: characterencoding filter and HiddenHttpMethodFilter. When registering in web.xml, you must first register CharacterEncodingFilter and then HiddenHttpMethodFilter, otherwise the problem of garbled code will still occur.

reason:
In the characterencoding Filter, set the character set through the request.setCharacterEncoding(encoding) method. The request.setCharacterEncoding(encoding) method requires that there should be no previous operation to obtain the request parameters, while the HiddenHttpMethodFilter has exactly one operation to obtain the request method: string paramvalue = request.getparameter (this. Methodparameter);. When multiple Filter filters are executed, their execution priority is determined by their top-down configuration in web.xml. If HiddenHttpMethodFilter is configured in the first place, there will be a garbled code problem.

3. Send delete request

Instead of giving examples, let's look at the complete case below.

3, RESTful case

(1) Preparatory work

first, Create a web module, configure web.xml and deploy Tomcat

Prepare entity class:

public class Employee {

    private Integer id;
    private String lastName;

    private String email;
    //1 male, 0 female
    private Integer gender;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Employee(Integer id, String lastName, String email, Integer gender) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
    }

    public Employee() {
    }
}

Prepare dao for simulation data and operation data:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.atguigu.mvc.bean.Employee;
import org.springframework.stereotype.Repository;


@Repository
public class EmployeeDao {

   private static Map<Integer, Employee> employees = null;
   
   static{
      employees = new HashMap<Integer, Employee>();

      employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
      employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
      employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
      employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
      employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
   }
   
   private static Integer initId = 1006;
   
   public void save(Employee employee){
      if(employee.getId() == null){
         employee.setId(initId++);
      }
      employees.put(employee.getId(), employee);
   }
   
   public Collection<Employee> getAll(){
      return employees.values();
   }
   
   public Employee get(Integer id){
      return employees.get(id);
   }
   
   public void delete(Integer id){
      employees.remove(id);
   }
}

controller:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;
}

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--In multiple Filter When filters are executed, the priority of their execution is determined by their web.xml The order of top-down configuration is determined-->

    <!--Register filter CharacterEncodingFilter-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--To set the encoding of the request to UTF-8-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <!--To set the encoding of the response to UTF-8-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--Register filter HiddenHttpMethodFilter-->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- to configure SpringMVC The front-end controller of the browser uniformly processes the requests sent by the browser -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Specified by initialization parameters SpringMVC Location and name of the configuration file -->
        <init-param>
            <!-- contextConfigLocation Is a fixed value -->
            <param-name>contextConfigLocation</param-name>
            <!-- use classpath:Indicates finding a configuration file from a classpath, for example maven In Engineering src/main/resources -->
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <!--As the core component of the framework, there are a lot of initialization operations to be done during startup
        These operations are performed only when the first request is made, which will seriously affect the access speed
        Therefore, it is necessary to start the control through this label DispatcherServlet The initialization time of is advanced until the server starts -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <!--set up springMVC The request path of the request that can be processed by the core controller of
        /The matching request can be/login or.html or.js or.css Mode request path
        however/Cannot match.jsp Request for request path -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

(2) Function list

functionURL addressRequest mode
Visit home page √/GET
Query all data √/employeeGET
Delete √/employee/2DELETE
Jump to add data page √/toAddGET
Execute save √/employeePOST
Jump to update data page √/employee/2GET
Execute update √/employeePUT

(3) Visit the home page

Add in springMVC.xml (introduce mvc namespace before this):

<!--Configure view controller-->
<mvc:view-controller path="/" view-name="index"/>

<!--open mvc Annotation driven-->
<mvc:annotation-driven />

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>home page</title>
</head>
<body>
<h1>home page</h1>
<a th:href="@{/employee}">View all employee information</a>
</body>
</html>

(4) Query all employee data

controller:

    @RequestMapping(value = "/employee",method = RequestMethod.GET)
    public String selectAllEmployees(Model model){
        Collection<Employee> employeeList = employeeDao.getAll();
        model.addAttribute("employeeList",employeeList);
        return "employee_list";
    }

employee_list.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1" cellpadding="0" cellspacing="0" style="text-align: center">
        <tr>
            <th colspan="5">Employee information</th>
        </tr>
        <tr>
            <th>Employee number</th>
            <th>full name</th>
            <th>mailbox</th>
            <th>Gender</th>
            <th>option</th>
        </tr>
        <tr th:each="employee:${employeeList}">
            <td th:text="${employee.id}"></td>
            <td th:text="${employee.lastName}"></td>
            <td th:text="${employee.email}"></td>
            <td th:text="${employee.gender}"></td>
            <td>
                <a href="">delete</a>
                <a href="">modify</a>
            </td>
        </tr>
    </table>
    <a href="">Add employee</a>
</body>
</html>

(5) Delete function

In employee_ Create a form to handle the delete request in list.html:

<!-- Function: control the submission of forms through hyperlinks post Request converted to delete request -->
<form id="delete_form" method="post">
    <!-- HiddenHttpMethodFilter Requirement: must be transmitted_method Request parameters, and the value is the final request method -->
    <input type="hidden" name="_method" value="delete"/>
</form>

next, Download vue.js And copy it to webapp/static/js /

employee_ Hyperlink in list.html for deletion:

<a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>

employee_list.html hyperlink binding click event for deletion:

<!--introduce vue.js,adopt vue Handle delete link click events-->
    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
    <script type="text/javascript">
        var vue = new Vue({
            el:"#dataTable",
            methods:{
                //Event indicates the current event
                deleteEmployee:function (event) {
                    //Get form elements by id
                    var deleteForm = document.getElementById("deleteForm");
                    //Assign the href attribute of the hyperlink that triggers the click event to the action of the form
                    deleteForm.action = event.target.href;
                    //Submit Form 
                    deleteForm.submit();
                    //Block default jump behavior of hyperlinks
                    event.preventDefault();
                }
            }
        });
    </script>

controller:

    @RequestMapping(value = "/employee/{id}",method = RequestMethod.DELETE)
    public String deleteEmployeeById(@PathVariable("id") Integer id){
        employeeDao.delete(id);
        return "redirect:/employee";
    }

springMVC.xml:

<!--Open access to static resources-->
<!--All requests were rejected DispatcherServlet Processing, the controller has no mapping to access static resources
 We're going to use it DefaultServlet handle-->
<mvc:default-servlet-handler />

The project has been run before, and the static resource vue.js added later does not exist in the packaging result. Repackage it (click packaging in Lifecycle of Maven interface in IDEA)

(6) Jump to the add data page

springMVC.xml:

<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>

Create employee_add.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>add employee</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    full name:<input type="text" name="lastName"><br>
    Email:<input type="text" name="email"><br>
    Gender:<input type="radio" name="gender" value="1">male
    <input type="radio" name="gender" value="0">female<br>
    <input type="submit" value="add to"><br>
</form>

</body>
</html>

(7) Execute save

controller:

@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){
    employeeDao.save(employee);
    return "redirect:/employee";
}

(8) Jump to update data page

employee_ Hyperlinks for modification in list.html:

<a th:href="@{'/employee/'+${employee.id}}">modify</a>

controller:

@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
    Employee employee = employeeDao.get(id);
    model.addAttribute("employee", employee);
    return "employee_update";
}

Create employee_update.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Update Employee</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="id" th:value="${employee.id}">
    full name:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
    mailbox:<input type="text" name="email" th:value="${employee.email}"><br>
    <!--
        th:field="${employee.gender}"Can be used for echo of radio boxes or check boxes
        If the radio box is value and employee.gender If the values are consistent, add checked="checked"attribute
    -->
    Gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
    <input type="submit" value="to update"><br>
</form>

</body>
</html>

(9) Execute update

controller:

@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
    employeeDao.save(employee);
    return "redirect:/employee";
}

Posted by fubowl on Thu, 02 Sep 2021 16:38:39 -0700