1. 程式人生 > 其它 >SpringMvc 有關 Restful 介面開發和呼叫總結

SpringMvc 有關 Restful 介面開發和呼叫總結

具體什麼是 Restful ,可以查詢一下百度百科,簡單的理解就是根據 Http 的請求方式(Get、Post、Put、Delete)來決定處理方式。Restful 的優點主要在於請求地址書寫簡化,隱藏資源的訪問和資料傳送細節,對網站有一定的保護作用。

Restful 在實際應用場景中多用於開發介面,大家習慣的約定為: Get 請求用於查詢,Post 請求用於新增,Put 請求用於修改,Delete 請求用於刪除。但是這僅僅是約定而已,並不是必須要遵守的規範,可以隨意打破。比如我個人就是喜歡使用 Post 請求開發所有介面(增刪改查),這也是可以的。

本篇部落格主要通過程式碼的方式演示 SpringMvc 如何開發 Restful 介面,頁面如何採用 form 表單呼叫 Restful 介面,頁面如果通過 ajax 請求非同步呼叫 Restful 介面。在本篇部落格的最後會提供 Demo 的原始碼下載。


一、搭建工程

新建一個 maven 專案,匯入相關 jar 包,我所匯入的 jar 包都是最新的,內容如下:

有關具體的 jar 包地址,可以在 https://mvnrepository.com 上進行查詢。

<dependencies>
    <!--匯入 servlet 相關的 jar 包-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>

    <!--匯入 Spring 核心 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.18</version>
    </dependency>
    <!--匯入 SpringMvc 的 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--匯入 jackson 相關的 jar 包-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.1</version>
    </dependency>
</dependencies>

配置好引用的 jar 包後,開啟右側的 Maven 視窗,重新整理一下,這樣 Maven 會自動下載所需的 jar 包檔案。

搭建好的專案工程整體目錄比較簡單,具體如下圖所示:

com.jobs.config 包下儲存的是 SpringMvc 的配置檔案和 Servlet 的初始化檔案
com.jobs.controller 包下儲存的是用於提供 api 介面的類
com.jobs.domain 包下儲存的是 JavaBean 實體類

web 目錄下放置的是網站檔案,只有一個靜態頁面和一些 js 檔案


二、SpringMvc 配置相關

com.jobs.config 下的 SpringMvcConfig 類是 SpringMvc 的配置類,具體內容如下:

package com.jobs.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

@Configuration
//讓 SpringMvc 僅僅掃描載入配置了 @Controller 註解的類
@ComponentScan(value = "com.jobs",
        includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = {Controller.class}))
//啟用 mvc 功能,配置了該註解之後,SpringMvc 攔截器放行相關資源的設定,才會生效
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {

    //配置 SpringMvc 聯結器放行常用資源的格式(圖片,js,css)
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //配置響應資料格式所對應的資料處理轉換器
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

        //如果響應的是 application/json ,則使用 jackson 轉換器進行自動處理
        MappingJackson2HttpMessageConverter jsonConverter =
                        new MappingJackson2HttpMessageConverter();
        jsonConverter.setDefaultCharset(Charset.forName("UTF-8"));
        List<MediaType> typelist1 = new ArrayList<>();
        typelist1.add(MediaType.APPLICATION_JSON);
        jsonConverter.setSupportedMediaTypes(typelist1);
        converters.add(jsonConverter);

        //如果響應的是 text/html 和 text/plain ,則使用字串文字轉換器自動處理
        StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
        stringConverter.setDefaultCharset(Charset.forName("UTF-8"));
        List<MediaType> typelist2 = new ArrayList<>();
        typelist2.add(MediaType.TEXT_HTML);
        typelist2.add(MediaType.TEXT_PLAIN);
        stringConverter.setSupportedMediaTypes(typelist2);
        converters.add(stringConverter);
    }

    //註解配置 SpringMvc 返回配置的字串所表示的頁面,從哪些去找
    //可以註釋掉下面的方法,這樣需要在 SpringMvc 方法返回時,指定全域性路徑的頁面地址
    //這裡配置的是:根據 SpringMvc 方法返回的字串,到 /WEB-INF/pages/ 下找對應名稱的 jsp 頁面
    @Bean
    public InternalResourceViewResolver getViewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        //如果頁面需要使用JSTL標籤庫的話
        //viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }
}

ServletInitConfig 類初始化 Servlet 容器,裝載 SpringMvc 的配置,具體如下:

package com.jobs.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.*;
import java.util.EnumSet;

public class ServletInitConfig extends AbstractDispatcherServletInitializer {

    //初始化 Servlet 容器,載入 SpringMvc 配置類
    //建立 web 專用的 Spring 容器物件:WebApplicationContext
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext cwa = new AnnotationConfigWebApplicationContext();
        cwa.register(SpringMvcConfig.class);
        return cwa;
    }

    //註解配置 SpringMvc 的 DispatcherServlet 攔截地址,攔截所有請求
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    //新增過濾器
    @Override
    protected Filter[] getServletFilters() {
        //採用 utf-8 作為統一請求的編碼
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");

        //該過濾器,能夠讓 web 頁面通過 _method 引數將 Post 請求轉換為 Put、Delete 等請求
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
    }
}

需要注意的是:這裡換了一種新增過濾器的方式(重寫了 getServletFilters 方法),在該方法中添加了兩個過濾器(統一請求編碼設定過濾器,以及允許將 Post 請求轉化為 Put、Delete 等請求的過濾器),其中這個過濾器:【允許將 Post 請求轉化為 Put、Delete 等請求】很重要,它是頁面發起 Restful 請求呼叫介面的基礎條件。


三、介面開發介紹

還是首選介紹一下 domian 下的 Employee 實體類,具體內容如下:

package com.jobs.domain;

import java.io.Serializable;
import java.util.List;

public class Employee implements Serializable {

    //姓名
    private String name;
    //年齡
    private Integer age;

    public Employee() {
    }

    public Employee(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    //這裡省略了相關欄位的 get 和 set 方法...

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

下面列出 Restful 介面的開發細節,RestfulController1 和 RestfulController2 開發的 Restful 介面的功能是相同的,只不過採用的註解不一樣。

RestfulController1 採用的是普通的註解,RestfulController2 採用的是簡化註解,具體內容如下:

package com.jobs.controller;

import com.jobs.domain.Employee;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/rest1")
public class RestfulController1 {

    //restful風格的介面,在路徑上可以包含請求引數
    //如果沒有註明接收的請求方式,則表示所有請求方式(Get,Post,Put,Delete)都支援
    //使用 @PathVariable 註解獲取路徑上配置的同名變數
    //produces 表示返回的資料型別
    @RequestMapping(value = "/{test}", produces = "text/plain")
    public String restfulTest1(@PathVariable String test) {
        System.out.println("獲取到引數值為:" + test);
        return "獲取到引數值為:" + test;
    }

    //僅僅支援 Get 請求,模擬通過 id 查詢員工
    //增加上 method = RequestMethod.GET 表示僅僅支援 Get 請求
    @RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
    public Employee restfulTest2(@PathVariable Integer id) {
        System.out.println("接收到 Get 提交的資料,id為:" + id);
        Employee emp = new Employee("任肥肥", 40);
        return emp;
    }

    //僅僅支援 Post 請求,模擬新增一條員工資訊(通過路徑引數獲取資料)
    //增加上 method = RequestMethod.POST 表示僅僅支援 Post 請求
    @RequestMapping(value = "/emp/{name}/{age}", method = RequestMethod.POST)
    public Employee restfulTest3(
            @PathVariable String name,
            @PathVariable Integer age) {
        Employee emp = new Employee(name, age);
        System.out.println("接收到 Post 提交的資料,封裝成物件:" + emp);
        return emp;
    }

    //僅僅支援 Post 請求,模擬新增一條員工資訊(通過請求的 body 獲取資料)
    //這次直接 Post 提交過來 json 資料,Json 的欄位名稱與 Employee 的欄位名稱一致
    @RequestMapping(value = "/emp", method = RequestMethod.POST, produces = "text/plain")
    public String restfulTest4(Employee emp) {
        System.out.println("接收到 Post 提交的資料,封裝成物件:" + emp);
        return "獲取 Json 資料,並封裝成功物件成功";
    }

    //僅僅支援 Put 請求,模擬修改一條員工資訊
    @RequestMapping(value = "/emp", method = RequestMethod.PUT)
    public Employee restfulTest5(Employee emp) {
        System.out.println("接收到 Put 提交的資料,封裝成物件:" + emp);
        //修改員工資訊並返回
        emp.setName(emp.getName() + "111");
        emp.setAge(emp.getAge() + 10);
        return emp;
    }

    //僅僅支援 Delete 請求,模擬通過 id 刪除一條員工資訊
    @RequestMapping(value = "/emp/{id}", method = RequestMethod.DELETE, produces = "text/plain")
    public String restfulTest6(@PathVariable Integer id) {
        System.out.println("接收到 Delete 提交的資料,id為:" + id);
        return "接收到 Delete 提交的資料,id為:" + id;
    }
}
package com.jobs.controller;

import com.jobs.domain.Employee;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/rest2")
public class RestfulController2 {

    //如果沒有註明接收的請求方式,則表示所有請求方式(Get,Post,Put,Delete)都支援
    //Restful 只是一種介面編寫的風格,並不是規範
    //大家習慣於 Get 請求用於查詢,Post 請求用於新增,Put 請求用於修改,Delete 請求用於刪除
    //實際上,你也可以不遵循這樣的規則,比如你可以使用 Post 請求執行查詢功能。
    @RequestMapping(value = "/{test}")
    public String restfulTest1(@PathVariable String test) {
        System.out.println("獲取到引數值為:" + test);
        return "獲取到引數值為:" + test;
    }

    //僅僅支援 Get 請求,模擬通過 id 查詢員工
    //可以使用 @GetMapping 註解,簡化 Get 請求編寫方式
    @GetMapping(value = "/emp/{id}")
    public Employee restfulTest2(@PathVariable Integer id) {
        System.out.println("接收到 Get 提交的資料,id為:" + id);
        Employee emp = new Employee("任肥肥", 40);
        return emp;
    }

    //僅僅支援 Post 請求,模擬新增一條員工資訊(通過路徑引數獲取資料)
    //可以使用 @PostMapping 註解,簡化 Post 請求編寫方式
    @PostMapping(value = "/emp/{name}/{age}")
    public Employee restfulTest3(
            @PathVariable String name,
            @PathVariable Integer age) {
        Employee emp = new Employee(name, age);
        System.out.println("接收到 Post 提交的資料,封裝成物件:" + emp);
        return emp;
    }

    //僅僅支援 Post 請求,模擬新增一條員工資訊(通過請求的 body 獲取資料)
    //這次直接 Post 提交過來 json 資料,Json 的欄位名稱與 Employee 的欄位名稱一致
    @PostMapping(value = "/emp")
    public String restfulTest4(Employee emp) {
        System.out.println("接收到 Post 提交的資料,封裝成物件:" + emp);
        return "獲取 Json 資料,並封裝成功物件成功";
    }

    //僅僅支援 Put 請求,模擬修改一條員工資訊
    //可以使用 @PutMapping 註解,簡化 Put 請求編寫方式
    @PutMapping(value = "/emp")
    public Employee restfulTest5(Employee emp) {
        System.out.println("接收到 Put 提交的資料,封裝成物件:" + emp);
        //修改員工資訊並返回
        emp.setName(emp.getName() + "111");
        emp.setAge(emp.getAge() + 10);
        return emp;
    }

    //僅僅支援 Delete 請求,模擬通過 id 刪除一條員工資訊
    //可以使用 @DeleteMapping 註解,簡化 Delete 請求編寫方式
    @DeleteMapping(value = "/emp/{id}")
    public String restfulTest6(@PathVariable Integer id) {
        System.out.println("接收到 Delete 提交的資料,id為:" + id);
        return "接收到 Delete 提交的資料,id為:" + id;
    }
}

編寫靜態 html 頁面,其中 form 表單呼叫的是 RestfulController1 的介面,超連結採用 ajax 請求非同步呼叫 RestfulController2 的介面,具體內容如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Restful介面測試</title>
</head>
<body>
    <h1>這裡是 SpringMvc 的 Restful 介面開發測試</h1>
    <fieldset>
        <legend>採用 Form 表單提交發起 Get 請求</legend>
        <form action="/rest1/emp/1" method="get">
            <input type="submit" value="發起 Get 請求"/>
        </form>
    </fieldset>
    <hr/>
    <fieldset>
        <legend>採用 Form 表單提交發起 Post 請求</legend>
        <form action="/rest1/emp/喬豆豆/30" method="post">
            <input type="submit" value="發起 Post 請求"/>
        </form>
    </fieldset>
    <hr/>
    <fieldset>
        <legend>採用 Form 表單提交發起 Post 請求</legend>
        <form action="/rest1/emp" method="post">
            name值:<input type="text" name="name" value="任肥肥"><br/>
            age值:<input type="text" name="age" value="40"><br/>
            <input type="submit" value="發起 Post 請求"/>
        </form>
    </fieldset>
    <hr/>
    <fieldset>
        <legend>採用 Form 表單提交發起 Put 請求</legend>
        <form action="/rest1/emp" method="Post">
            <input type="hidden" name="_method" value="put"/>
            name值:<input type="text" name="name" value="候胖胖"><br/>
            age值:<input type="text" name="age" value="42"><br/>
            <input type="submit" value="發起 Put 請求"/>
        </form>
    </fieldset>
    <hr/>
    <fieldset>
        <legend>採用 Form 表單提交發起 Delete 請求</legend>
        <form action="/rest1/emp/100" method="Post">
            <input type="hidden" name="_method" value="delete"/>
            <input type="submit" value="發起 Delete 請求"/>
        </form>
    </fieldset>
    <hr/>
    <fieldset>
        <legend>使用 Ajax 傳送 Restful 請求</legend>
        <a href="javascript:void(0);" id="test1">傳送Get請求查詢資料</a><br/>
        <a href="javascript:void(0);" id="test2">傳送Post請求新增資料(從路徑上獲取資料)</a><br/>
        <a href="javascript:void(0);" id="test3">傳送Post請求新增資料(傳送 json 資料)</a><br/>
        <fieldset>
            <legend>Ajax 傳送 Put 請求</legend>
            提交資料時,需要參加引數:_method:put
            <a href="javascript:void(0);" id="test4">傳送Put請求修改資料(傳送 json 資料)</a><br/>
        </fieldset>
        <fieldset>
            <legend>Ajax 傳送 Delete 請求</legend>
            提交資料時,需要參加引數:_method:delete
            <a href="javascript:void(0);" id="test5">傳送Delete請求刪除資料</a><br/>
        </fieldset>
    </fieldset>
    <script src="./js/jquery-3.6.0.min.js"></script>
    <script src="./js/apitest.js"></script>
</body>
</html>

在該靜態頁面中,引用了 js 目錄中編寫的 apitest.js 檔案,具體內容如下:

$(function () {
    $('#test1').click(function () {
        $.ajax({
            type: "get",
            url: "/rest2/emp/100",
            dataType: "json",
            success: function (data) {
                alert("返回的資料:" + data.name + "," + data.age);
            }
        });
    });

    $('#test2').click(function () {
        $.ajax({
            type: "post",
            url: "/rest2/emp/喬豆豆/30",
            //如果沒有指定 dataType ,
            //則伺服器會自動根據介面返回的 mime 型別,推斷返回的資料型別
            success: function (data) {
                alert("返回的資料:" + data.name + "," + data.age);
            }
        });
    });

    $('#test3').click(function () {
        $.ajax({
            type: "post",
            url: "/rest2/emp",
            data: {name: "任肥肥", age: 40},
            dataType: "text", //伺服器介面返回的是 text 型別的資料
            success: function (data) {
                alert(data);
            }
        });
    });

    $('#test4').click(function () {
        $.ajax({
            type: "post",
            url: "/rest2/emp",
            data: {name: "任肥肥", age: 40, _method: "put"},
            dataType: "json", //伺服器介面返回的是 json 型別的資料
            success: function (data) {
                alert("返回的資料:" + data.name + "," + data.age);
            }
        });
    });

    $('#test5').click(function () {
        $.ajax({
            type: "post",
            url: "/rest2/emp/88",
            data: {_method: "delete"},
            dataType: "text", //伺服器介面返回的是 text 型別的資料
            success: function (data) {
                alert(data);
            }
        });
    });

})

然後執行網站,在首頁 index.html 靜態頁面中,即可進行介面的測試。也可以採用 Postman 工具進行測試。



到此為止,有關從 SpringMvc 開發和呼叫 Restful 介面,已經介紹完畢。希望對大家有所幫助。

本篇部落格的 Demo 原始碼下載地址為:https://files.cnblogs.com/files/blogs/699532/SpringMvc_Restful.zip