超詳細的JavaWeb使用者的增刪改查實現總結

阿福聊程式設計發表於2020-07-22

前言

本文是基於單體架構實現的角色的增刪改查的功能。前臺使用Bootstrap+Ajax+Jsp , 後端使用Spring+SpringMvc+MyBatis進行開發,相信使用過這些技術的小夥伴應該很好的理解以下的內容,也希望看了這篇文章小夥伴們有所收穫。

準備工作

後端技術

技術 說明 官網
Spring Spring是一個輕量級控制反轉(IoC)和麵向切面(AOP)的容器框架。 https://spring.io/
SpringMvc MVC框架 https://spring.io/projects/sp...
MyBatis 持久層框架 https://mybatis.org/mybatis-3...
Druid 資料庫連線池 https://github.com/alibaba/druid
log4j 日誌框架 https://logging.apache.org/lo...

前端技術

Bootstrap 簡潔、直觀、強悍的前端開發框架 https://www.bootcss.com/
Ajax 前後端互動 https://www.w3school.com.cn/a...
Jsp 模板引擎 https://www.runoob.com/jsp/js...
layer.js 訊息提示 http://www.h-ui.net/lib/layer...
Modal外掛 模態框(Modal)是覆蓋在父窗體上的子窗體 https://www.runoob.com/bootst...
jquery.pagination.js 分頁外掛 http://www.jq22.com/yanshi5697/

角色維護-分頁實現

分頁前端功能實現

建立外部JavaScript原始檔,role.js

在頁面 role-page.jsp引入role.js檔案

<script type="text/javascript" src="script/my-role.js"></script>

初始化全域性函式

分頁實現初始化全域性函式,每頁的條數,頁碼,模糊查詢的關鍵詞

//初始化全域性變數
function initGlobalVariable() {
    window.pageSize = 5; //每頁的條數
    window.pageNum = 1;  //頁碼
    window.keyword = ""; //關鍵詞
}

宣告分頁函式

//給伺服器傳送請求獲取分頁資料(pageInfo),並在頁面上顯示分頁效果(主體、頁碼導航條)
function showPage() {

    // 給伺服器傳送請求獲取分頁資料:PageInfo
    var pageInfo = getPageInfo();

    // 在頁面上的表格中tbody標籤內顯示分頁的主體資料
    generateTableBody(pageInfo);

    // 在頁面上的表格中tfoot標籤內顯示分頁的頁碼導航條
    initPagination(pageInfo);
}

獲取分頁資料

function getPageInfo() {

    // 以同步請求方式呼叫$.ajax()函式並獲取返回值(返回值包含全部響應資料)
    var ajaxResult = $.ajax({
        "url": "role/search/by/keyword.action",
        "type": "post",
        "data": {
            "pageNum": (window.pageNum == undefined) ? 1 : window.pageNum,
            "pageSize": (window.pageSize == undefined) ? 5 : window.pageSize,
            "keyword": (window.keyword == undefined) ? "" : window.keyword
        },
        "dataType": "json",
        "async": false    // 為了保證getPageInfo()函式能夠在Ajax請求拿到響應後獲取PageInfo,需要設定為同步操作
    });
    
    // 從全部響應資料中獲取JSON格式的響應體資料
    var resultEntity = ajaxResult.responseJSON;

    // 從響應體資料中獲取result,判斷當前請求是否成功
    var result = resultEntity.result;

    // 如果成功獲取PageInfo
    if (result == "SUCCESS") {
        return resultEntity.data;
    }

    if (result == "FAILED") {
        layer.msg(resultEntity.message);
    }

    return null;
}

使用PageInfo資料在tbody標籤內顯示分頁資料

function generateTableBody(pageInfo) {
    // 執行所有操作前先清空
    $("#roleTableBody").empty();   //這個對應頁面的 <tbody id="roleTableBody"> </tbody>
    // 獲取資料集合
    var list = pageInfo.list;

    // 判斷list是否有效
    if (list == null || list.length == 0) {
        $("#roleTableBody").append("<tr><td colspan='4' style='text-align:center;'>沒有查詢到資料!</td></tr>");
        return;
    }

    for (var i = 0; i < list.length; i++) {

        var role = list[i];

        var checkBtn = "<button type='button' class='btn btn-success btn-xs'><i class=' glyphicon glyphicon-check'></i></button>";
        var pencilBtn = "<button type='button' id='roleTableBody'  roleid='" + role.id + "' class='btn btn-primary btn-xs  editBtn'><i class=' glyphicon glyphicon-pencil'></i></button>";
        var removeBtn = "<button type='button'   roleid='" + role.id + "'  class='btn btn-danger btn-xs  removeBtn'><i class=' glyphicon glyphicon-remove'></i></button>";

        var numberTd = "<td>" + (i + 1) + "</td>";
        var checkBoxTd = "<td><input class='itemBox' roleid='" + role.id + "' type='checkbox'></td>";
        var roleNameTd = "<td>" + role.name + "</td>";
        var btnTd = "<td>" + checkBtn + " " + pencilBtn + " " + removeBtn + "</td>";

        var tr = "<tr>" + numberTd + checkBoxTd + roleNameTd + btnTd + "</tr>";

        // 將前面拼好的HTML程式碼追加到#roleTableBody中
        $("#roleTableBody").append(tr);
    }
}

宣告函式封裝導航條初始化操作


function initPagination(pageInfo) {

    // 宣告變數儲存分頁導航條顯示時的屬性設定
    var paginationProperties = {
        num_edge_entries: 3,            //邊緣頁數
        num_display_entries: 5,        //主體頁數
        callback: pageselectCallback,    //回撥函式
        items_per_page: window.pageSize,    //每頁顯示資料數量,就是pageSize
        current_page: (window.pageNum - 1),//當前頁頁碼
        prev_text: "上一頁",            //上一頁文字
        next_text: "下一頁"            //下一頁文字
    };

    // 顯示分頁導航條 <div id="Pagination" class="pagination"> <!-- 這裡顯示分頁 --> </div>
    $("#Pagination").pagination(pageInfo.total, paginationProperties);
}

在每一次點選“上一頁”、“下一頁”、“頁碼”時執行這個函式跳轉頁面

function pageselectCallback(pageIndex, jq) {

    // 將全域性變數中的pageNum修改為最新值
    // pageIndex從0開始,pageNum從1開始
    window.pageNum = pageIndex + 1;

    // 呼叫分頁函式重新執行分頁
    showPage();

    return false;
}

頁面初始化,就是我們點選角色維護頁面需要載入的內容

$(function(){
    
    // 呼叫分頁引數初始化方法
    initGlobalVariable();
    // 執行分頁
    showPage();
});

關鍵詞查詢功能

在點選“查詢”按鈕後,獲取文字框中填寫的keyword值,賦值給全域性變數keyword,呼叫showPage()函式即可。

//關鍵字查詢實現
        $("#searchBtn").click(function () {
            //獲取關鍵字查詢的值
            var keywordInput = $.trim($("#keywordInput").val());
            /*if (keywordInput==null || keywordInput==""){
                layer.msg("請輸入關鍵詞");
                return;
            }*/
            window.keyword = keywordInput;
            //執行查詢操作
            showPage();

        });

分頁後端實現

點選角色維護載入頁面資料兩種思路:

第一種是我們請求後臺把查詢到的資料放到Model,前臺遍歷把資料展示出來。

第二種是我們請求後臺把查詢到的資料當PageInfo<Role>,然後動態的拼接把資料展示到頁面上。(我們採用第二種)

Controller方法的實現

@ResponseBody
    @RequestMapping("/role/search/by/keyword")
    public ResultEntity<PageInfo<Role>> search(
            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
            @RequestParam(value = "keyword", defaultValue = "") String keyword) {

        // 1.查詢得到PageInfo物件
        PageInfo<Role> pageInfo = roleService.queryForKeywordWithPage(pageNum, pageSize, keyword);

        // 2.封裝結果物件返回
        return ResultEntity.successWithData(pageInfo);
    }

Service方法的實現

public PageInfo<Role> queryForKeywordWithPage(Integer pageNum, Integer pageSize, String keyword) {
        // 1.開啟分頁功能
        PageHelper.startPage(pageNum, pageSize);

        // 2.執行查詢
        List<Role> list = roleMapper.selectForKeywordSearch(keyword);

        // 3.封裝為PageInfo物件
        return new PageInfo<Role>(list);
    }

Mapper方法的實現

List<Role> selectForKeywordSearch(String keyword);

Mapper.xml

<select id="selectForKeywordSearch" resultMap="BaseResultMap">
        SELECT
            id,
            `name`
        FROM
            t_role
        WHERE
            `name` LIKE CONCAT('%', #{keyword}, '%')
</select>

角色維護-全選功能

功能在頁面的位置

具體實現

標記

role-page.jsp

<thead>
    <tr>
        <th width="30">#</th>
        <th width="30"><input id="summaryBox" type="checkbox"></th>
        <th>名稱</th>
        <th width="100">操作</th>
    </tr>
</thead>

my-role.js

for (var i = 0; i < list.length; i++) {
        //省略
        var checkBoxTd = "<td><input class='itemBox' roleid='" + role.id + "' type='checkbox'></td>";
       //省略
}

給summaryBox繫結單擊響應函式

 //全選/全不選功能實現
 $("#summaryBox").click(function () {
            //獲取當前的選中狀態
            var currentStatus = this.checked;
            $(".itemBox").prop("checked", currentStatus);
 });

角色維護-批量刪除

準備模態框

先準備模態框的HTML標籤,include-modal-role-confirm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<div id="confirmModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal"
                        aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">角色維護刪除</h4>
            </div>
            <div class="modal-body">
                <p>您確定要刪除下面的顯示的內容嗎?</p>
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th width="30">#</th>
                        <th>名稱</th>
                    </tr>
                    </thead>
                    <tbody id="confirmModalTableBody"></tbody>
                </table>
            </div>
            <div class="modal-footer">
                <button id="confirmModalBtn" type="button" class="btn btn-primary">OK</button>
            </div>
        </div>
    </div>
</div>

在role-page.jsp中包含include-modal-role-confirm.jsp檔案, <%@ include file="/WEB-INF/include-modal-role-confirm.jsp" %>

getRoleListByRoleIdArray()函式

//id查詢角色資訊
function getRoleListByRoleIdArray(roleIdArray) {
    //roleIdArray轉換成JSON字串
    var roleIds = JSON.stringify(roleIdArray);

    var ajaxResult = $.ajax({
        "url": "role/get/list/by/id/list.action",
        "type": "post",
        "data": roleIds,
        "contentType": "application/json;charset=UTF-8",
        "dataType": "json",
        "async": false
    });
    // 3.獲取JSON物件型別的響應體
    var resultEntity = ajaxResult.responseJSON;
    var result = resultEntity.result;
    if (result == "SUCCESS") {

        // 5.如果成功,則返回roleList
        return resultEntity.data;
    }

    if (result == "FAILED") {
        layer.msg(resultEntity.message);
        return null;
    }

    return null;

}

對應的後端程式碼:

 @ResponseBody
    @RequestMapping("role/get/list/by/id/list")
    public ResultEntity<List<Role>> getRoleListByIdList(@RequestBody List<Integer> roleIds) {
        List<Role> roleList = roleService.findRoleListByIdList(roleIds);
        return ResultEntity.successWithData(roleList);
 }
public List<Role> findRoleListByIdList(List<Integer> roleIds) {

        return roleMapper.findRoleListByIdList(roleIds);
    }

showRemoveConfirmModal()函式

// 開啟刪除確認模態框
function showRemoveConfirmModal() {
    // 1.將模態框顯示出來
    $("#confirmModal").modal("show");
    //獲取角色資料
    var roleList = getRoleListByRoleIdArray(window.roleIdArray);
    //清空表格資料
    $("#confirmModalTableBody").empty();
    //填充confirmModalTableBody的資料
    for (var i = 0; i < roleList.length; i++) {
        // 5.獲取角色相關資料
        var role = roleList[i];

        var id = role.id;
        var name = role.name;
        var trHTML = "<tr><td>" + (i+1) + "</td><td>" + name + "</td></tr>";
        // 6.執行填充
        $("#confirmModalTableBody").append(trHTML);

    }
}

點選批量刪除按鈕繫結單擊響應函式

標記批量刪除按鈕

 <button type="button" class="btn btn-danger" id="batchRemoveBtn"
                            style="float: right; margin-left: 10px;">
                        <i class=" glyphicon glyphicon-remove"></i> 刪除
 </button>

檢查itemBox是否被選中

 // 給批量刪除按鈕繫結單擊響應函式
        $("#batchRemoveBtn").click(function () {
            //獲取被選中的itemBox陣列長度
            var length = $(".itemBox:checked").length;
            if (length == 0) {
                layer.msg("請選擇要刪除的記錄!!");
                return;
            }
           // 未完待續...
        });

在彈出的模態框中顯示confirm資訊

 // 給批量刪除按鈕繫結單擊響應函式
        $("#batchRemoveBtn").click(function () {
            //獲取被選中的itemBox陣列長度
            var length = $(".itemBox:checked").length;
            if (length == 0) {
                layer.msg("請選擇要刪除的記錄!!");
                return;
            }
            window.roleIdArray = new Array();
            //遍歷核取方塊
            $(".itemBox:checked").each(function () {
                //通過checkbox的roleid屬性獲取roleId值
                var roleId = $(this).attr("roleid");
                //存入陣列
                window.roleIdArray.push(roleId);
            });
            // 呼叫函式開啟模態框
            showRemoveConfirmModal();

        });

點選模態框的OK按鈕執行刪除

標記OK按

 <button **id="confirmModalBtn"**  type="button" class="btn  btn-primary">OK</button>  

繫結單擊響應函式

$("#confirmModalBtn").click(function () {
            //陣列轉成Json
            var roleIds = JSON.stringify(window.roleIdArray);
            var ajaxResult = $.ajax({
                "url": "role/batch/remove.action",
                "type": "post",
                "data": roleIds,
                "contentType": "application/json;charset=UTF-8",
                "dataType": "json",
                "async": false,
                "success": function (response) {
                    var result = response.result;
                    if (result == "SUCCESS") {
                        layer.msg("操作成功!");
                        // 如果刪除成功,則重新呼叫分頁方法
                        showPage();
                    }
                    if (result == "FAILED") {
                        layer.msg(response.message);
                    }
                    // 不管成功還是失敗,都需要關掉模態框
                    $("#confirmModal").modal("hide");
                },
                "error": function (response) {
                    if (result == "FAILED") {
                        layer.msg(response.message);
                    }
                }

            });

        }); 

後端程式碼

 @ResponseBody
    @RequestMapping(value = "role/batch/remove")
    public ResultEntity<String> batchAdminList(@RequestBody List<Integer> roleIds) {
        try {
            roleService.batchRoleList(roleIds);
            return ResultEntity.successWithoutData();
        } catch (Exception e) {
            return ResultEntity.failed(null, e.getMessage());
        }
    }
public void batchRoleList(List<Integer> roleIds) {
        roleMapper.batchRoleList(roleIds);
    }
<delete id="batchRoleList" parameterType="java.util.List">
        delete from  t_role where id in
        <foreach collection="list" item="item" open="(" separator="," close=")" >
           #{item}
        </foreach>
</delete>

角色維護-新增

大體步驟

  • 給“新增”按鈕繫結單擊響應函式
  • 開啟模態框
  • 給“儲存”按鈕繫結單擊響應函式
  • 收集文字框內容
  • 傳送請求
  • 請求處理完成關閉模態框、重新分頁、清理表單

給新增按鈕繫結單擊響應函式

標記新增按鈕

 <button type="button" class="btn btn-primary" id="addBtn"
                            style="float: right;">
                        <i class="glyphicon glyphicon-plus"></i> 新增
 </button>

繫結單擊響應函式

 $("#addBtn").click(function(){                              alert("aaa...");                          });   

準備模態框

先準備模態框的HTML程式碼,include-modal-role-add.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<div id="addModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <form role="form">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"
                            aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                    <h4 class="modal-title">角色新增</h4>
                </div>
                <div class="modal-body">
                    <input type="text" id="roleNameInput" class="form-control" placeholder="請輸入角色名稱" />
                </div>
                <div class="modal-footer">
                    <button type="button" id="addModalBtn" class="btn btn-default"><i class="glyphicon glyphicon-plus"></i> 儲存</button>
                    <button type="reset" class="btn btn-primary"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
                </div>
            </form>
        </div>
    </div>
</div>

將include-modal-role-add.jsp包含到role-page.jsp , <%@ include file="/WEB-INF/include-modal-role-add.jsp" %>

開啟模態框

 $("#addBtn").click(function(){              $("#addModal").modal("show");         });  

給“儲存”按鈕繫結單擊響應函式

標記“儲存”按鈕

  <button id="addModalBtn"  type="button" class="btn btn-success">       <i  class="glyphicon glyphicon-plus"></i>儲存  </button>  

繫結單擊響應函式

$("#addModalBtn").click(function () {

            // 1.收集文字框內容
            var roleName = $.trim($("#roleNameInput").val());

            if (roleName == null || roleName == "") {
                layer.msg("請輸入有效角色名稱!");
                return;
            }

            // 2.傳送請求
            $.ajax({
                "url": "role/save/role.action",
                "type": "post",
                "data": {
                    "roleName": roleName
                },
                "dataType": "json",
                "success": function (response) {

                    var result = response.result;

                    if (result == "SUCCESS") {
                        layer.msg("操作成功!");

                        // 3.操作成功重新分頁
                        // 前往最後一頁
                        window.pageNum = 999999;
                        showPage();
                    }

                    if (result == "FAILED") {
                        layer.msg(response.message);
                    }

                    // 4.不管成功還是失敗,關閉模態框
                    $("#addModal").modal("hide");

                    // 5.清理本次在文字框填寫的資料
                    $("#roleNameInput").val("");

                },
                "error": function (response) {
                    layer.msg(response.message);
                }
            });

        });

後端部分程式碼

 @ResponseBody
    @RequestMapping("role/save/role")
    public ResultEntity<String> saveRole(@RequestParam("roleName") String roleName) {
        try {
            roleService.saveRole(roleName);
            return ResultEntity.successWithoutData();
        } catch (Exception e) {
            return ResultEntity.failed(null, e.getMessage());
        }
    }

公共返回程式碼

public class ResultEntity<T> {
    public static final String SUCCESS = "SUCCESS";
    public static final String FAILED = "FAILED";
    public static final String NO_MESSAGE = "NO_MESSAGE";
    public static final String NO_DATA = "NO_DATA";

    // 方便返回成功結果(不攜帶查詢結果情況)
    public static ResultEntity<String> successWithoutData() {
        return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
    }

    // 方便返回成功結果(攜帶查詢結果情況)
    public static <E> ResultEntity<E> successWithData(E data) {
        return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
    }

    // 方便返回失敗結果
    public static <E> ResultEntity<E> failed(E data, String message) {
        return new ResultEntity<E>(FAILED, message, data);
    }

    private String result;
    private String message;
    private T data;

    public ResultEntity() {

    }

    public ResultEntity(String result, String message, T data) {
        super();
        this.result = result;
        this.message = message;
        this.data = data;
    }

    @Override
    public String toString() {
        return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

}

角色維護-更新

大體步驟

給編輯按鈕繫結單擊響應函式

開啟模態框

  • ​ 準備模態框
  • ​ 把roleId儲存到全域性變數
  • ​ 獲取到當前按鈕所在行的roleName
  • ​ 使用roleName回顯模態框中的表單
  • ​ 給“更新”按鈕繫結單擊響應函式
  • ​ 收集文字框內容
  • ​ 傳送請求
  • ​ 請求處理完成關閉模態框、重新分頁

給編輯按鈕繫結單擊響應函式

標記編輯按鈕

my-role.js檔案

function generateTableBody(pageInfo) {
        //省略
        var pencilBtn = "<button type='button'   roleid='" + role.id + "' class='btn btn-primary btn-xs  editBtn'><i class=' glyphicon glyphicon-pencil'></i></button>";
      //省略
    }
}

準備模態框

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<div id="editModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <form role="form">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"
                            aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                    <h4 class="modal-title">尚籌網系統彈窗</h4>
                </div>
                <div class="modal-body">
                    <input type="text" id="roleNameInputEdit" class="form-control"
                           placeholder="請輸入角色名稱" />
                </div>
                <div class="modal-footer">
                    <button id="editModalBtn" type="button" class="btn btn-warning">
                        <i class="glyphicon glyphicon-edit"></i> 更新
                    </button>
                    <button type="reset" class="btn btn-primary">
                        <i class="glyphicon glyphicon-refresh"></i> 重置
                    </button>                </div>
            </form>
        </div>
    </div>
</div>

將include-modal-role-add.jsp包含到role-page.jsp , <%@ include file="/WEB-INF/include-modal-role-edit.jsp" %>

繫結單擊響應函式

 $("#roleTableBody").on("click", ".editBtn", function () {

            // 1.獲取當前按鈕的roleId
            window.roleId = $(this).attr("roleId");

            // 2.獲取當前按鈕所在行的roleName
            var roleName = $(this).parents("tr").children("td:eq(2)").text();

            // 3.修改模態框中文字框的value值,目的是在顯示roleName
            $("#roleNameInputEdit").val(roleName);

            // 4.開啟模態框
            $("#editModal").modal("show");
        });

給“更新”按鈕繫結單擊響應函式

$("#editModalBtn").click(function () {

            // 1.獲取文字框值
            var roleName = $.trim($("#roleNameInputEdit").val());

            if (roleName == null || roleName == "") {
                layer.msg("請輸入有效角色名稱!");

                return;
            }

            // 2.傳送請求
            $.ajax({
                "url": "role/update.action",
                "type": "post",
                "data": {
                    "id": window.roleId,
                    "name": roleName
                },
                "dataType": "json",
                "success": function (response) {
                    var result = response.result;

                    if (result == "SUCCESS") {
                        layer.msg("操作成功!");

                        // 3.操作成功重新分頁
                        showPage();
                    }

                    if (result == "FAILED") {
                        layer.msg(response.message);
                    }

                    // 4.不管成功還是失敗,關閉模態框
                    $("#editModal").modal("hide");

                }
            });
        });

後端部分程式碼

 @ResponseBody
    @RequestMapping("role/update")
    public ResultEntity<String> updateRole(@RequestParam("id") Integer id,
                                           @RequestParam("name") String name) {
        Role role = new Role();
        role.setId(id);
        role.setName(name);
        try {
            roleService.updateRole(role);
            return ResultEntity.successWithoutData();
        } catch (Exception e) {
            return ResultEntity.failed(null, e.getMessage());
        }
    }

異常對映相容非同步請求

問題表現

Ajax請求在伺服器端處理過程中丟擲異常,經過異常處理器:

@ControllerAdvice
public class CrowdFundingExceptionResolever {
    
    @ExceptionHandler(value=Exception.class)
    public ModelAndView catchException(Exception exception) {
        
        ModelAndView mav = new ModelAndView();
        
        mav.addObject("exception", exception);
        
        mav.setViewName("system-error");
        
        return mav;
    }

}

目前這個異常處理機制,只能返回頁面,而不能針對Ajax請求返回JSON格式的響應資料。所以Ajax請求處理過程中,如果丟擲異常,返回異常資訊頁面,Ajax程式無法正常解析,導致頁面不能正常顯示和工作,也不能給出友好的錯誤提示。

問題解決思路

非同步請求特點

分辨非同步請求的工具方法

    /**
     * 用於判斷一個請求是否是非同步請求
     * @param request
     * @return
     */
    public static boolean checkAsyncRequest(HttpServletRequest request) {
        
        // 1.獲取相應請求訊息頭
        String accept = request.getHeader("Accept");
        String xRequested = request.getHeader("X-Requested-With");
        
        // 2.判斷請求訊息頭資料中是否包含目標特徵
        if(
            (stringEffective(accept) && accept.contains("application/json")) 
            || 
            (stringEffective(xRequested) && xRequested.contains("XMLHttpRequest")) ) {
            return true;
        }
        
        return false;
    }

    /**
     * 判斷字串是否有效
     * @param source 待驗證字串
     * @return true表示有效,false表示無效
     */
    public static boolean stringEffective(String source) {
        
        return source != null && source.length() > 0;
    }

升級後的異常處理器

首先引入:

<dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
</dependency>
@ControllerAdvice
public class CrowdFundingExceptionResolever {
    @ExceptionHandler(value = Exception.class)
    public ModelAndView catchException(
            Exception exception,
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        // 1.對當前請求進行檢查
        boolean checkAsyncRequestResult = CrowdFundingUtils.checkAsyncRequest(request);

        // 2.如果是非同步請求
        if(checkAsyncRequestResult) {

            // 根據異常型別在常量中的對映,使用比較友好的文字顯示錯誤提示訊息
            String exceptionClassName = exception.getClass().getName();

            String message = CrowdFundingConstant.EXCEPTION_MESSAGE_MAP.get(exceptionClassName);

            if(message == null) {
                message = "系統未知錯誤";
            }

            // 3.建立ResultEntity物件
            ResultEntity<String> resultEntity = ResultEntity.failed(ResultEntity.NO_DATA, message);

            // 4.將resultEntity轉換為JSON格式
            Gson gson = new Gson();
            String json = gson.toJson(resultEntity);

            // 5.將json作為響應資料返回給瀏覽器
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(json);

            return null;
        }

        ModelAndView mav = new ModelAndView();

        mav.addObject("exception", exception);

        mav.setViewName("system-error");

        return mav;
    }
}

常量類

public class CrowdFundingConstant {
    
    public static final Map<String, String> EXCEPTION_MESSAGE_MAP = new HashMap<String, String>();

    static {
        EXCEPTION_MESSAGE_MAP.put("java.lang.ArithmeticException", "系統在進行數學運算時發生錯誤");
        EXCEPTION_MESSAGE_MAP.put("java.lang.RuntimeException", "系統在執行時發生錯誤");
        EXCEPTION_MESSAGE_MAP.put("com.atguigu.crowd.funding.exception.LoginException", "登入過程中執行錯誤");
    }
}

我是阿福,公眾號「阿福聊程式設計」作者,對後端技術保持學習愛好者,我會經常更新JAVA技術文章,在進階的路上,共勉!

相關文章