尚筹网-6.Role维护和异步请求的异常映射
尚筹网-6.Role维护和异步请求的异常映射
1 html和json扩展名问题
1.1 对应*.html扩展名的请求
返回一个完整页面的请求。
1.2 对应*.json扩展名的请求
返回JSON数据的请求。
{"result":"SUCCESS","message":"NO_MESSAGE","data":
{"pageNum":2,"pageSize":5,"size":5,"startRow":6,"endRow":10,"total":100,"pages":20,"list":
[{"id":6,"name":"role5"},{"id":7,"name":"role6"},{"id":8,"name":"role7"},{"id":9,"name":"role8"},
{"id":10,"name":"role9"}],"firstPage":1,"prePage":1,"nextPage":3,"lastPage":8,"isFirstPage":false,
"isLastPage":false,"hasPreviousPage":true,"hasNextPage":true,"navigatePages":8,"navigatepageNums":
[1,2,3,4,5,6,7,8]}}
1.3 匹配错误时的问题
如果请求扩展名是*.html,但是实际返回的响应体是JSON格式,Tomcat会认为实际返回的响应和响应消息头中的内容类型不匹配,会抛异常。
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
此时要解决这个问题,将返回JSON数据的请求扩展名设置为*.json或Tomcat无法识别的扩展名即可。
例如:xxx.json或xxx.rgh都可以。
2. 角色维护-关键词查询功能
2.1 基本思路
在点击“查询”按钮后,获取文本框中填写的keyword值,赋值给全局变量keyword,调用showPage()函数即可。
给“查询”按钮标记id
给“查询”按钮绑定单击响应函数
在单击响应函数中获取文本框中输入的数据
验证输入数据是否有效
如果无效,提示,停止函数执行
如果有效,赋值给window.keyword
调用showPage()
2.2 代码
$("#searchBtn").click(function(){
// 在单击响应函数中获取文本框中输入的数据
var keyword = $.trim($("#keywordInput").val());
// 验证输入数据是否有效
if(keyword == null || keyword == "") {
// 如果无效,提示,停止函数执行
layer.msg("请输入关键词!");
return ;
}
// 如果有效,赋值给window.keyword
window.keyword = keyword;
// 调用showPage()重新分页
showPage();
});
layer.msg('xxx')无法显示的原因
3 角色维护-全选功能
3.1 功能在页面的位置
3.2 具体实现
3.2.1 标记
总checkbox:/atcrowdfunding-admin-1-webui/src/main/webapp/WEB-INF/role-page.jsp
#
名称
操作
itembox:/atcrowdfunding-admin-1-webui/src/main/webapp/script/my-role.js
// 使用PageInfo数据在tbody标签内显示分页数据
function generateTableBody(pageInfo) {
……
for(var i = 0; i < list.length; i++) {
……
var checkBoxTd = " ";
……
}
}
3.2.2 给#summaryBox绑定单击响应函数
// 全选/全不选功能
$("#summaryBox").click(function(){
// 1.获取当前checkbox的选中状态
var currentStatus = this.checked;
// 2.设置itemBox的选中状态
$(".itemBox").prop("checked",currentStatus);
});
4. 角色维护-批量删除
4.1 准备工作
4.1.1 准备模态框
创建include-modal-role-confirm.jsp文件保存模态框内容。
在role-page.jsp中包含include-modal-role-confirm.jsp文件。
……
<%@ include file="/WEB-INF/include-modal-role-confirm.jsp" %>
4.1.2 getRoleListByRoleIdArray()函数
// 根据roleIdArray查询roleList
function getRoleListByRoleIdArray(roleIdArray) {
// 1.将roleIdArray转换成JSON字符串
var requestBody = JSON.stringify(roleIdArray);
// 2.发送Ajax请求
var ajaxResult = $.ajax({
"url":"role/get/list/by/id/list.json",
"type":"post",
"data":requestBody,
"contentType":"application/json;charset=UTF-8",
"dataType":"json",
"async":false
});
// 3.获取JSON对象类型的响应体
var resultEntity = ajaxResult.responseJSON;
// 4.验证是否成功
var result = resultEntity.result;
if(result == "SUCCESS") {
// 5.如果成功,则返回roleList
return resultEntity.data;
}
if(result == "FAILED") {
layer.msg(resultEntity.message);
return null;
}
return null;
}
对应的后端代码:
com.rgh.crowd.funding.handler.RoleHandler
// handler方法
@ResponseBody
@RequestMapping("/role/get/list/by/id/list")
public ResultEntity> getRoleListByIdList(@RequestBody List roleIdList) {
List roleList = roleService.getRoleListByIdList(roleIdList);
return ResultEntity.successWithData(roleList);
}
com.rgh.crowd.funding.service.impl.RoleServiceImpl
// service方法
@Override
public List getRoleListByIdList(List roleIdList) {
// 预期的SQL语句
// select id,name from t_role where id in (1,2,3,6,12)
// 创建实体类Role对应的Example对象
RoleExample roleExample = new RoleExample();
// 在Example对象中封装查询条件
roleExample.createCriteria().andIdIn(roleIdList);
// 执行查询
return roleMapper.selectByExample(roleExample);
}
4.1.3 showRemoveConfirmModal()函数
// 打开删除确认模态框
function showRemoveConfirmModal() {
// 1.将模态框显示出来
$("#confirmModal").modal("show");
// 2.根据roleIdList获取roleList
var roleList = getRoleListByRoleIdArray(window.roleIdArray);
// 3.清空#confirmModalTableBody
$("#confirmModalTableBody").empty();
// 4.填充#confirmModalTableBody
for(var i = 0; i < roleList.length; i++) {
// 5.获取角色相关数据
var role = roleList[i];
var id = role.id;
var name = role.name;
var trHTML = ""+id+" "+name+" ";
// 6.执行填充
$("#confirmModalTableBody").append(trHTML);
}
}
4.2 给批量删除按钮绑定单击响应函数
4.2.1 标记批量删除按钮
批量删除
4.2.2 检查itemBox是否被选中
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function(){
// 获取被选中的itemBox数组长度
var length = $(".itemBox:checked").length;
// 如果长度为0,说明没有选择itemBox
if(length == 0) {
layer.msg("请至少选择一条!");
return ;
}
// 未完待续...
});
4.2.3 在弹出的模态框中显示confirm信息
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function(){
// 获取被选中的itemBox数组长度
var length = $(".itemBox:checked").length;
// 如果长度为0,说明没有选择itemBox
if(length == 0) {
layer.msg("请至少选择一条!");
return ;
}
// 在全局作用域内创建roleIdArray
window.roleIdArray = new Array();
// 遍历$(".itemBox:checked")
$(".itemBox:checked").each(function(){
// 通过checkbox的roleid属性获取roleId值
var roleId = $(this).attr("roleid");
// 存入数组
window.roleIdArray.push(roleId);
});
// 调用函数打开模态框
showRemoveConfirmModal();
});
4.3 点击模态框的OK按钮执行删除
4.3.1 标记OK按钮
OK
4.3.2 绑定单击响应函数
// 给确认模态框中的OK按钮绑定单击响应函数
$("#confirmModalBtn").click(function(){
var requestBody = JSON.stringify(window.roleIdArray);
$.ajax({
"url":"role/batch/remove.json",
"type":"post",
"data":requestBody,
"contentType":"application/json;charset=UTF-8",
"dataType":"json",
"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){
layer.msg(response.message);
}
});
});
4.3.3 后端代码
com.rgh.crowd.funding.handler.RoleHandler
@ResponseBody
@RequestMapping("/role/batch/remove")
public ResultEntity batchRemove(@RequestBody List roleIdList) {
roleService.batchRemove(roleIdList);
return ResultEntity.successWithoutData();
}
com.rgh.crowd.funding.service.impl.RoleServiceImpl
@Override
public void batchRemove(List roleIdList) {
RoleExample roleExample = new RoleExample();
roleExample.createCriteria().andIdIn(roleIdList);
roleMapper.deleteByExample(roleExample);
}
5 角色维护-单条删除
// 针对.removeBtn这样动态生成的元素对象使用on()函数方式绑定单击响应函数
// $("动态元素所依附的静态元素").on("事件类型","具体要绑定事件的动态元素的选择器", 事件响应函数);
$("#roleTableBody").on("click",".removeBtn", function(){
// 获取当前记录的roleId
var roleId = $(this).attr("roleId");
// 存入全局变量数组
window.roleIdArray = new Array();
window.roleIdArray.push(roleId);
// 打开模态框(后续所有操作都和批量删除一样)
showRemoveConfirmModal();
});
6 角色维护-新增
6.1 大体步骤
给“新增”按钮绑定单击响应函数
打开模态框
给“保存”按钮绑定单击响应函数
请求处理完成关闭模态框、重新分页、清理表单
6.2 给“新增”按钮绑定单击响应函数
6.2.1 标记“新增”按钮
新增
6.2.2 绑定单击响应函数
$("#addBtn").click(function(){
alert("aaa...");
});
6.3 打开模态框
6.3.1 准备模态框
先准备模态框的HTML代码
创建/atcrowdfunding-admin-1-webui/src/main/webapp/WEB-INF/include-modal-role-add.jsp文件
将include-modal-role-add.jsp包含到role-page.jsp
<%@ include file="/WEB-INF/include-modal-role-add.jsp" %>
6.3.2 打开模态框
$("#addBtn").click(function(){
$("#addModal").modal("show");
});
6.4 给“保存”按钮绑定单击响应函数
6.4.1 标记“保存”按钮
保存
6.4.2 绑定单击响应函数
$("#addModalBtn").click(function(){
// 1.收集文本框内容
var roleName = $.trim($("#roleNameInput").val());
if(roleName == null || roleName == "") {
layer.msg("请输入有效角色名称!");
return ;
}
// 2.发送请求
$.ajax({
"url":"role/save/role.json",
"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);
}
});
});
6.5 后端代码
com.rgh.crowd.funding.handler.RoleHandler
@ResponseBody
@RequestMapping("/role/save/role")
public ResultEntity saveRole(@RequestParam("roleName") String roleName) {
roleService.saveRole(roleName);
return ResultEntity.successWithoutData();
}
com.rgh.crowd.funding.service.impl.RoleServiceImpl
@Override
public void saveRole(String roleName) {
roleMapper.insert(new Role(null, roleName));
}
7 角色维护-更新
7.1 大体步骤
7.2 给“铅笔”按钮绑定单击响应函数
7.2.1 标记“铅笔”按钮
找到/atcrowdfunding-admin-1-webui/src/main/webapp/script/my-role.js文件
function generateTableBody(pageInfo) {
……
for(var i = 0; i < list.length; i++) {
……
var pencilBtn = " ";
……
}
}
7.2.2 准备模态框
<%@ include file="/WEB-INF/include-modal-role-edit.jsp" %>
7.2.3 绑定单击响应函数
$("#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");
});
7.3 给“更新”按钮绑定单击响应函数
7.3.1 前端代码
$("#editModalBtn").click(function(){
// 1.获取文本框值
var roleName = $.trim($("#roleNameInputEdit").val());
if(roleName == null || roleName == "") {
layer.msg("请输入有效角色名称!");
return ;
}
// 2.发送请求
$.ajax({
"url":"role/update/role.json",
"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");
}
});
});
7.3.2 后端代码
@ResponseBody
@RequestMapping("/role/update/role")
public ResultEntity updateRole(Role role) {
roleService.updateRole(role);
return ResultEntity.successWithoutData();
}
@Override
public void updateRole(Role role) {
roleMapper.updateByPrimaryKey(role);
}
8 @RestController
相当于@Controller注解+@ResponseBody注解,类使用了@RestController之后相当于在每一个方法上都加了@ResponseBody注解。
9 异常映射兼容异步请求
9.1 问题表现
Ajax请求在服务器端处理过程中抛出异常,经过异常处理器:
com.rgh.crowd.funding.exception.CrowFundingExceptionResolever
@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程序无法正常解析,导致页面不能正常显示和工作,也不能给出友好的错误提示。
9.2 问题解决思路
9.3 异步请求特点
9.4 分辨异步请求的工具方法
在atcrowdfunding-admin-3-common工程加入servlet-api依赖
javax.servlet
servlet-api
provided
com.rgh.crowd.funding.util.CrowdFundingUtils
/**
* 用于判断一个请求是否是异步请求
* @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;
}
9.5 升级后的异常处理器
@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) {
// 3.创建ResultEntity对象
ResultEntity resultEntity = ResultEntity.failed(ResultEntity.NO_DATA, exception.getMessage());
// 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;
}
}
※需要Gson支持
com.google.code.gson
gson
2.8.5
9.6 改进提示消息
所在工程:atcrowdfunding-admin-3-common
全类名:com.rgh.crowd.funding.util.CrowdFundingConstant
public static final Map EXCEPTION_MESSAGE_MAP = new HashMap<>();
static {
EXCEPTION_MESSAGE_MAP.put("java.lang.ArithmeticException", "系统在进行数学运算时发生错误");
EXCEPTION_MESSAGE_MAP.put("java.lang.RuntimeException", "系统在运行时发生错误");
EXCEPTION_MESSAGE_MAP.put("com.rgh.crowd.funding.exception.LoginException", "登录过程中运行错误");
}
所在工程:atcrowdfunding-admin-2-component
全类名:com.rgh.crowd.funding.exeption.CrowdFundingExceptionResolever
@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 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;
}
}
10 登录拦截器兼容异步请求
问题的产生和解决的思路都和异常映射部分一致
com.rgh.crowd.funding.interceptor.LoginInterceptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 通过request对象获取HttpSession对象
HttpSession session = request.getSession();
// 从Session域尝试获取已登录用户对象
Admin admin = (Admin) session.getAttribute(CrowdFundingConstant.ATTR_NAME_LOGIN_ADMIN);
// 如果没有获取到Admin对象
if(admin == null) {
// 进一步判断当前请求是否是异步请求
boolean checkAsyncRequestResult = CrowdFundingUtils.checkAsyncRequest(request);
if(checkAsyncRequestResult) {
// 为异步请求的响应创建ResultEntity对象
ResultEntity resultEntity = ResultEntity.failed(ResultEntity.NO_DATA, CrowdFundingConstant.MESSAGE_ACCESS_DENIED);
// 创建Gson对象
Gson gson = new Gson();
// 将ResultEntity对象转换为JSON字符串
String json = gson.toJson(resultEntity);
// 设置响应的内容类型
response.setContentType("application/json;charset=UTF-8");
// 将JSON字符串作为响应数据返回
response.getWriter().write(json);
// 表示不能放行,后续操作不执行
return false;
}
// 将提示消息存入request域
request.setAttribute(CrowdFundingConstant.ATTR_NAME_MESSAGE, CrowdFundingConstant.MESSAGE_ACCESS_DENIED);
// 转发到登录页面
request.getRequestDispatcher("/WEB-INF/admin-login.jsp").forward(request, response);
return false;
}
// 如果admin对象有效,则放行继续执行后续操作
return true;
}
11 分页的showPage()函数修正
// 给服务器发送请求获取分页数据(pageInfo),并在页面上显示分页效果(主体、页码导航条)
function showPage() {
// 给服务器发送请求获取分页数据:PageInfo
var pageInfo = getPageInfo();
console.log(pageInfo);
if(pageInfo == null) {
// 如果没有获取到pageInfo数据,则停止后续操作
return ;
}
// 在页面上的表格中tbody标签内显示分页的主体数据
generateTableBody(pageInfo);
// 在页面上的表格中tfoot标签内显示分页的页码导航条
initPagination(pageInfo);
}
你可能感兴趣的:(尚筹网-6.Role维护和异步请求的异常映射)