1 课程计划
- 高级参数绑定
- 数组类型的参数绑定
- List类型的绑定
- @RequestMapping注解的使用
- Controller方法返回值
- Springmvc中异常处理机制
- 图片上传处理
- Josn数据交互
- Springmvc实现restful
- 拦截器
2 高级参数绑定
2.1 数组的绑定
2.1.1 需求分析
根据选中的商品ID进行删除
2.1.2 页面的修改
<input type="checkbox" name="ids" value="${user.id}" > |
2.1.3 controller的编写
@RequestMapping("/batchDeleteSave") public String batchDeleteSave(Long[] ids,UserVo vo) { System.out.println("---");
return "success"; } |
2.1.4 userMpper.xml的编写
<!-- 批量删除用户 batchDeleteUser --> <delete id="batchDeleteUser" parameterType="Long[]" > delete from user <where> <foreach collection="array" item="id" open="and id in(" close=")" separator="," > #{id} </foreach> </where> </delete> |
2.2 将表单的数据绑定到List(了解)
2.2.1 需求分析
把数据绑定到List集合
2.2.2 修改Pojo
private List<User> users;
public List<User> getUsers() { return users; }
public void setUsers(List<User> users) { this.users = users; } |
2.2.3 修改jsp页面
<tr> <td>用户名称</td> <td><input type="text" name="users[0].username" value="${user.username }" /></td> </tr> <tr> <td>生日</td> <td><input type="text" name="users[0].birthday" value="<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd" />" /></td> </tr> <tr> <td>性别</td> <td> <select name="users[0].sex" > <option value="1" <c:if test="${user.sex=='1'}">selected="selected"</c:if> >男</option> <option value="2" <c:if test="${user.sex=='2'}">selected="selected"</c:if> >女</option> </select> </td> </tr> |
2.2.4 controller编写
@RequestMapping(value="/addUser",method=RequestMethod.POST) public String addUserVo(UserVo vo) { System.out.println("...."); return "addUser"; } |
3 RequestMapping
作用:通过requestMapping注解可以定义不同的处理器映射规则。
3.1 窄化请求映射
可以在class上面加上RequestMapping(url)指定通用的请求前缀。
@Controller @RequestMapping("/user") public class UserController { |
3.2 URL路径映射和请求方法限制
@RequestMapping(value={"/ceshi1","/ceshi2"},method=RequestMethod.POST) public String ceshi(UserVo vo) { System.out.println("...."); return "addUser"; } |
4 controller 方法返回值
4.1 返回ModelAndView或者Model
Controller方法中定义ModelAndView对象并返回,对象中可添加model数据,指定view视图。
//model可以配合返回值进行逻辑视图的跳转。 model.addAttribute("name","张三"); //return "url"
//modelAndView modelAndView.setViewName("url"); modelAndView.addObject("name","李四"); |
4.2 返回void
在controller方法形参上可以定义request和reresponse,使用requset和response指定响应结果。
//转发 request.getRequestDispatcher("/WEB-INF/adduser.jsp").forward(request, response);
//重定向 response.sendRedirect("/user/addUser.do");
//返回json格式的数据 response.setCharacterEncoding("utf-8"); response.setContentType("application/json;charset=utf-8"); //设置响应正文的编码格式 // response.setContentType("text/json;charset=utf-8") response.getWriter().println("/{/"aa"/:/"bb"/}/"); jackson fastJSON |
|
Ajax接受服务端响应的json字符串,{“username”:”张三”,”password”:”123”}; [object][object]
var data =xmlHttpRequest.ResponseText;
alert(data); {“username”:”张三”,”password”:”123”}
怎么把JSON字符串编程对象? Var data = eval(“(“+data+”)”)
data.username
客户端使用的是原生ajax xmlHttpRequest
var data = xmlHttpRequest.ResponseXml
客户端可以接受的响应数据格式
- Json
- xml
- HTML片段
4.2.1 返回逻辑视图名
Controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
return "url"; |
4.2.2 重定向
Contrller方法返回结果重定向到另外一个url地址。
return "redirect:/user/getUserList.do"; |
4.2.3 转发(用得比较少)
return "forward:/user/getUserList.do"; |
注意:逻辑试图返回的字符串默认就是转发
5 异常处理器
Springmvc在处理请求过程中出现异常信息通常交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
5.1 异常处理思路
系统中异常包括两类。预期异常和运行时异常RuntimeException,前面的通过捕获异常从而获取异常信息,后面主要通过代码规范开发,测试减少运行时异常的发生。
异常处理思路图:
5.2 自定义异常类
为了区别不同的异常通常会根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller service dao抛出异常说明是系统预期处理的异常信息。
public CustomException() { }
public CustomException(String message) { this.message = message; }
//异常信息 private String message;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; } |
5.3 自定义异常处理器
要实现全局异常处理,就要实现HandlerExceptionResolver接口。
String message = null;
//1.判断抛出的异常是否是系统自定义异常 if(ex instanceof CustomException) { CustomException c = (CustomException)ex; message = c.getMessage(); }else { //2.如果不是系统自定义的异常 //3.打印异常信息 ex.printStackTrace();
//把异常信息输出 StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); //写日志 log4j logback message = sw.toString(); }
ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", message); modelAndView.setViewName("error"); return modelAndView; |
5.4 错误页面
<body> <!-- 错误页面 --> <h6>错误页面:</h6> <h1>${message}</h1> </body> |
5.5 异常处理器配置
<!-- 配置全局异常处理器 --> <bean class="com.whhp.springmvc.exception.CloabException" /> |
5.6 异常测试
public String ceshi5() throws CustomException { //1. 测试自定义异常 // if(true) { // throw new CustomException("系统错误,请等待!"); // } //2. 非自定义异常 int i = 1/0; return "success"; } |
6 图片上传
图片上传的三要素:
- 必须要有文件上传项
- 表单的请求方式必须是POST
- Form表单必须有一个属性 enctype=”multipart/form-data”
6.1 编写jsp页面
<form id="itemForm" action="/springmvc-02/addUser.do" method="post" enctype="multipart/form-data" > <input type="hidden" name="id" value="${user.id }" /> 添加用户信息: <table width="100%" border=1> <tr> <td>用户名称</td> <td><input type="text" name="users[0].username" value="${user.username }" /></td> </tr> <tr> <td>生日</td> <td><input type="text" name="users[0].birthday" value="<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd" />" /></td> </tr> <tr> <td>性别</td> <td> <select name="users[0].sex" > <option value="1" <c:if test="${user.sex=='1'}">selected="selected"</c:if> >男</option> <option value="2" <c:if test="${user.sex=='2'}">selected="selected"</c:if> >女</option> </select> </td> </tr> <tr> <td>上传图像</td> <td><input type="file" name="picfile"/></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="提交" /> </td> </tr> </table> </form> |
6.2 导入jar包
6.3 配置解析器
因为springmvc默认不支持图片上传,所以需要配置解析器。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!—文件最大不能超过5兆 --> <property name="maxUploadSize" value="5242880" /> </bean> |
6.4 编写后台业务逻辑代码
1. 在class上面需要配置@MultiparConfig
2. MultipartFile的名称必须和表单的name保持一致
3. 创建图片保存的位置
代码的编写:
@RequestMapping(value="/addUser",method=RequestMethod.POST) public String addUserVo(MultipartFile picfile,HttpServletRequest request) throws IllegalStateException, IOException { //1. 获得图片的名称 String filename = picfile.getOriginalFilename();
//2. 获得图片保存在服务器的路径 String realPath = request.getServletContext().getRealPath("/imgs");
//3. 把图片上传到服务器 picfile.transferTo(new File(realPath+"/"+filename));
return "addUser"; } |
7 json数据交互
response.setContentTyep(‘’text/xml;charset=utf-8’);
7.1 RequestBody
作用:
requestBody注解用于将json格式的请求参数绑定controller的方法的参数上面。
请求参数,{“username”:”张三”,”address”:”武汉宏鹏”} 可以通过requestBody标签使用对象来接收数据。底层是通过springmvc的HttpMessageConverter接口将读取到的内容转换成json,xml绑定到方法的参数上。
7.2 ResponseBody
作用:
可以将响应的数据转换成为json响应给客户端。
7.3 请求和响应Json的案例
7.3.1 页面的编写
<button onclick="sendJsonTest();" >提交Json数据</button> <script type="text/javascript"> function sendJsonTest() { $.ajax({ type:"post", url:"${pageContext.request.contextPath}/user/ceshi7.do", contentType:"application/json;charset=utf-8", success:function(data) { alert(data); } }); }
</script> |
评价:这里应该是掉了一个data
7.3.2 controller的编写
@RequestMapping("/ceshi7.do") @ResponseBody public User ceshi7(@RequestBody User user) { System.out.println(user.toString()); return user; } |
8 RESTful支持
8.1 什么是restful
一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
资源定位:互联网所有的事物都是资源,要求URL中没有动词,只有名次,没有参数。
访问查询用户 http://localost/springmvc02/user/selectUserById.do?id=1
Restful软件架构风格:http://localhost/springmvc02/user/selectUserById/1
资源操作:使用put delete post get使用不同方法对资源进行操作。分别对应添加,删除,修改,查询。
8.2 使用restful风格实现通过ID查询
8.2.1 修改servlet-mapping
<servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- 拦截任何资源,但是不包扣jsp --> <url-pattern>/</url-pattern> </servlet-mapping> |
8.2.2 controller的编写
/** * 使用restful风格实现通过ID查询用户 */ @RequestMapping("/selectUserById/{id}") public void selectUserById(@PathVariable("id") String id) { //去数据库查询就可以了 System.out.println(id); } |
评价:这个@PathVariable 主要就是做一个参数的映射
8.2.3 静态资源访问
<!-- 静态资源访问 --> <mvc:resources location="/js/" mapping="/js/**"/> |
评价:由于上面拦截了所有的资源,所有这里报出了未定义$的错误,这里需要再加载静态资源
9 拦截器
springWebMvc的处理器拦截器类似于过滤器Filter,用于对处理器进行预处理和后处理。
9.1 自定义拦截器
需要实现HanderInterceptor接口
/** * handler执行前调用此方法 * 如果返回true表示继续执行,返回false停止执行 * 实际开发中可以加入登录校验,权限拦截等。。。 */ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("CustomInterceptor1 preHandler"); return false; }
/** * handler执行后但未返回视图前调用此方法 * 这里可以在返回视图前对模型数据进行处理,比如可以加入通用的信息进行页面的显示 */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView modelAndView) throws Exception { System.out.println("CustomInterceptor1 postHandle"); }
/** * hadnler执行后且视图返回后调用次方法 * 这里记录记录操作日志,资源清理,还可以得到异常信息。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("CustomInterceptor1 afterCompletion"); } |
import com.github.pagehelper.StringUtil;
import com.urovo.config.Constants;
import com.urovo.exception.TokenException;
import com.urovo.model.TokenModel;
import com.urovo.util.i18n.I18nMessage;
import com.urovo.util.token.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class AccessTokenInterceptor extends HandlerInterceptorAdapter {
@Autowired
private I18nMessage i18nMessage;
@Autowired
private TokenUtils tokenUtils;
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//token校验
if ((((HandlerMethod) handler).getBeanType().getName().equals("com.urovo.controller.LoginController"))) {
String ac_token = request.getHeader(Constants.AUTH_CODE_token);
Object o = redisTemplate.boundValueOps(Constants.AUTH_CODE + ac_token).get();
if (o != null) {
String code = (String) o;
request.setAttribute(Constants.CURRENT_REQUEST_KAPTCHA, code);
}
return true;
}
//从请求参数和请求头中取出uid,token
String uid = request.getHeader(Constants.UID);
String token = request.getHeader(Constants.AUTHORIZATION);
if (StringUtil.isEmpty(token) || StringUtil.isEmpty(uid)) {
throw new TokenException(i18nMessage.getMessage("token.uid.none"));
}
//redis token 校验
TokenModel tm = new TokenModel(uid, token);
if (tokenUtils.checkToken(tm)) {
request.setAttribute(Constants.CURRENT_USER_ID, tm.getUserId());
request.setAttribute(Constants.CURRENT_USER_TOKEN, tm.getToken());
//从缓存获取公司和公司子节点编号数据
String company_node = (String)redisTemplate.boundValueOps(Constants.USER_COMPANY_NODE+tm.getUserId()).get();
String[] split = company_node.split(":");
request.setAttribute(Constants.CURRENT_USER_COMPANY,split[0]);
if ( split.length > 1 ) {
if ( "null".equalsIgnoreCase(split[1])) {
split[1] = null;
}
request.setAttribute(Constants.CURRENT_USER_COMPANY_NODE,split[1]);
}
return true;
} else {
throw new TokenException(i18nMessage.getMessage("token.invalid"));
}
}
}
9.2 多重拦截器
多重拦截器的执行顺序:
CustomInterceptor1 preHandler CustomInterceptor2 preHandler 123 CustomInterceptor2 postHandle CustomInterceptor1 postHandle CustomInterceptor2 afterCompletion CustomInterceptor1 afterCompletion |
9.3 拦截器的实际应用
需求分析:用户登录的拦截
- 有一个登录页面,需要写一个controller访问页面
- 登录页面有一个提交的表单,需要在controller中处理。
- 判断用户名和密码是否正确
- 如果正确在session中写入用户信息
- 返回登录成功
3 拦截器
-
- 拦截用户请求,判断用户是否登录
- 如果用户已经登录,放行。
- 如果用户未登录,那么需要跳转到登录页面。
9.3.1 登录页面的编写
<h3>用户登录:</h3> <form action="/springmvc-02/user/login.do" method="post" > 用户名:<input type="text" name="username" ></br> 密码:<input type="password" name="password" /> <input type="submit" value="提交" > </form> |
9.3.2 后台代码的编写
Controller代码的编写:
@RequestMapping(value="/login",method=RequestMethod.POST) public String userLogin(String username,String password,HttpSession session) { //用户登录成功,把用户的信息放入session域 session.setAttribute("username",username); return "success"; } |
9.3.3 拦截器的编写
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果是访问的登录页面,那么就放行 String url = request.getRequestURI(); if(url.contains("login")) { return true; } //如果用户已经登录那么也放行 Object usernmae = request.getSession().getAttribute("username"); if(usernmae != null) { return true; }
//用户没有登录跳转到登录页面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } |