SpringMVC(二)
一、绑定数组和List
1、绑定数组参数
通过id数组批量删除
<form action="${pageContext.request.contextPath }/deleteAll.action" method="post"> <table width="100%" border=1> <tr> <td>
<input type="submit" value="批量删除"/>
</td> </tr> </table> <table width="100%" border=1> <c:forEach items="${itemList }" var="item" varStatus="status"> <tr> <!-- name属性名称要等于vo中的接收的id数组属性名 --> <td> <input type="checkbox" name="ids" value="${item.id }"/> </td> </tr> </c:forEach> </table> </form>
// 窄化请求映射 localhost:8081/ssm0523-1/items/deleteAll.action @RequestMapping("/items") public class ItemsController { @Autowired private ItemsService itmesService; @RequestMapping("/deleteAll") public String delAll(QueryVo vo) throws Exception{ //如果批量删除,一堆input复选框,那么可以提交数组.(只有input复选框被选中的时候才能提交) System.out.println(vo); return ""; // 不跳转页面 } }
包装类QueryVo中有private Integer[] ids属性
2、绑定List集合
通过List<Items>集合批量修改
包装类QueryVo中有private List<Items> itemList;
<form action="${pageContext.request.contextPath }/updateAll.action" method="post"> <!-- status.index循环次数 --> <c:forEach items="${itemList }" var="item" varStatus="status"> <table width="100%" border=1> <tr> <!-- name属性名称要等于vo中的接收的属性名 --> <!-- 如果批量删除,可以用List<pojo>来接收,页面上input框的name属性值= vo中接收的集合属性名称+[list的下标]+.+list泛型的属性名称 --> <td> <input type="checkbox" name="ids" value="${item.id }"/> <!-- 隐藏域,不显示,但是提交 --> <input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/> </td> <td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td> <td><input type="text" name="itemsList[${status.index }].price" value="${item.price }"/></td> <td><input type="text" name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td> <td><input type="text" name="itemsList[${status.index }].detail" value="${item.detail }"/></td> <td><a href="${pageContext.request.contextPath }/items/itemEdit/${item.id}">修改</a></td> </tr> </table> </c:forEach> </form>
@RequestMapping("/updateAll") public String updateAll(QueryVo vo) throws Exception{ System.out.println(vo); return ""; }
二、@RequestMapping注解
定义不同的处理器映射规则
1、请求URL路径映射(加在方法上)
@RequestMapping(value="/item")或@RequestMapping("/item)
value的值是数组,可以将多个url映射到同一个方法,只有一个值时可以不写value
2、窄化请求路径(加在类上)
@RequestMapping(value="/item")
请求的URL前缀,类下所有的所有的方法都要加上此前缀,/item/itemList
3、限定请求方法
@RequestMapping(method = RequestMethod.GET) 限定GET方式
用POST报错405
三、Controller方法返回值
指定返回到的页面和返回到页面的数据
1、ModelAndView
modelAndView.addObject("itemList", list); 指定返回页面的数据
modelAndView.setViewName("itemList"); 指定返回的页面
2、String(推荐使用)
返回普通字符串,就是页面去掉扩展名的名称, 返回给页面数据通过Model来完成
返回的字符串以forward:开头为请求转发
返回的字符串以redirect:开头为重定向
3、返回void(使用它破坏了springMvc的结构,所以不建议使用)
可以使用request.setAttribut 来给页面返回数据
可以使用request.getRquestDispatcher().forward()来指定返回的页面
如果controller返回值为void则不走springMvc的组件,所以要写页面的完整路径名称(/WEB-INF/)
相对路径:相对于当前目录,也就是在当前类的目录下,这时候可以使用相对路径跳转
绝对路径:从项目名后开始
在springMvc中不管是forward还是redirect后面凡是以/开头的为绝对路径,不以/开头的为相对路径
forward:/items/itemEdit.action 为绝对路径
forward:itemEdit.action为相对路径
四、异常处理(架构级别)
主要为了防止项目上线后给用户抛500等异常信息,所以需要在架构级别上整体处理.hold住异常
首先自定义全局异常处理器实现HandlerExceptionResolver接口
在spirngMvc.xml中配置生效
五、上传图片
1、配置虚拟目录
在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:
<Context docBase="F:\develop\upload\temp" path="/pic" reloadable="false"/>
访问http://localhost:8080/pic即可访问F:\develop\upload\temp下的图片
也可以通过eclipse配置
2、导包
CommonsMultipartResolver依赖commons-fileupload-1.2.2.jar、commons-io-2.4.jar
3、配置解析器
springmvc.xml中
<!--文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--设置上传文件的最大尺寸为5MB --> <property name="maxUploadSize"> <value>5242880</value> </property> </bean>
4、图片上传
@RequestMapping("/updateitem") public String update(MultipartFile pictureFile,Items items, Model model, HttpServletRequest request) throws Exception{ //1. 获取图片完整名称 String fileStr = pictureFile.getOriginalFilename(); //2. 使用随机生成的字符串+源图片扩展名组成新的图片名称,防止图片重名 String newfileName = UUID.randomUUID().toString() + fileStr.substring(fileStr.lastIndexOf(".")); //3. 将图片保存到硬盘 File uploadPic = new java.io.File("F:/develop/upload/temp/"+newFileName); if(!uploadPic.exists()){ uploadPic.mkdirs(); } pictureFile.transferTo(uploadPic); //4.将图片名称保存到数据库 items.setPic(newfileName); itmesService.updateItems(items); return "itemEdit" }
5、JSP页面
form添加enctype="multipart/form-data"
<form id="itemForm" action="${pageContext.request.contextPath }/item/editItemSubmit.action" method="post"enctype="multipart/form-data"> <input type="hidden" name="pic" value="${item.pic }"/> </form>
file的name与controller形参一致
<tr> <td>商品图片</td> <td><c:if test="${item.pic !=null}"> <imgsrc="/pic/${item.pic}" width=100 height=100 /> <br/> </c:if><input type="file" name="pictureFile" /></td> </tr>
六 JSON数据交互
@RequestBody
将页面传到Controller中的JSON格式字符串自动转换成java的POJO对象,并绑定到Controller方法的参数上
@ResponseBody
将Java中POJO对象自动转换成JSON格式字符串返回给页面,通过response响应给客户端
@Controller @RequestMapping("/items") public class ItemsController { @Autowired private ItemsService itmesService; //导入jackson的jar包在 controller的方法中可以使用@RequestBody,让spirngMvc将json格式字符串自动转换成java中的pojo //页面json的key要等于java中pojo的属性名称 //controller方法返回pojo类型的对象并且用@ResponseBody注解,springMvc会自动将pojo对象转换成json格式字符串 @RequestMapping("/sendJson") @ResponseBody public Items json(@RequestBody Items items) throws Exception{ System.out.println(items); return items; } }
七、Restful开发
资源定位及资源操作的风格,不是标志,也不是协议,对HTTP协议的诠释
就是对URL的命名标准,要求url中只有能名词,没有动词(不严格要求),但是要求url中不能用问号?传参
传参数:
页面:${pageContext.request.contextPath }/items/itemEdit/${item.id}
Controller方法中接收: @RquestMapping("/itemEdit/{id}")
方法: @PathVariable("id") Integer idd
/**
*通过@PathVariable可以接收url中传入的参数 *@RequestMapping("/itemEdit/{id}")中接收参数使用大括号中加上变量名称, @PathVariable中的变量名称要和RequestMapping中的变量名称相同 */ @RequestMapping("/itemEdit/{id}") 请求的URL public String itemEdit(@PathVariable("id") Integer id, HttpServletRequest reuqest, Model model) throws Exception{ @PathVariable获得URL的数据
}
${pageContext.request.contextPath }/items/itemEdit/${item.id}
八、拦截器
类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,一般做登录权限验证时用的比较多
1、需要编写自定义拦截器类,实现HandlerInterceptor接口
2、在spirngMvc.xml中配置拦截器生效
实现HandlerInterceptor接口
public class Interceptor implements HandlerInterceptor{ /** * controller执行前调用此方法,ModelAndView没有被返回 * 返回true表示继续执行,返回false中止执行 * 这里可以加入登录校验、权限拦截等 */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { } /** * controller执行后但未返回视图ModelAndView前调用此方法 * 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示 */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndViewmodelAndView) throws Exception { } /** * controller执行后且视图ModelAndView返回后调用此方法 * 这里可得到执行controller时的异常信息 * 这里可记录操作日志,资源清理等 */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
springmvc.xml中配置拦截器
<mvc:interceptors> <!-- 可配置多个拦截器,顺序执行 --> <mvc:interceptor> <!-- 拦截请求的路径 要拦截所有必需配置成/** --> <mvc:mapping path="/**"/> <!-- 指定拦截器的位置 --> <bean class="cn.guojie.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
九、登录权限验证
1、编写登录的controller, 编写跳转到登录页面的方法, 编写登录验证方法
2、编写登录页面
3、编写拦截器
运行过程:
(1)访问随意一个页面,拦截器会拦截请求,会验证session中是否有登录信息
如果已登录,放行
如果未登录,跳转到登录页面
(2)在登录页面中输入用户名,密码,点击登录按钮,拦截器会拦截请求,如果是登录路径放行
在controller方法中判断用户名密码是否正确,如果正确则将登录信息放入session
@Controller @RequestMapping("/login") public class LoginController { //跳转到登录页面 @RequestMapping("/login") public String login() throws Exception{ return "login"; } @RequestMapping("/submit") public String submit(String username, String pwd ,HttpServletRequest request) throws Exception{ HttpSession session = request.getSession(); //判断用户名密码的正确性,如果正确则将登录信息放入session中 //这里简写,真正项目中需要去数据库中校验用户名和密码 if(username != null){ session.setAttribute("username", username); } //跳转到列表页显示 重定向 return "redirect:/items/list"; } }
public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { //判断当前访问路径是否为登录的路径,如果是则放行 if(request.getRequestURI().indexOf("/login") > 0){ return true; } //判断session中是否有登录信息,如果没有则跳转到登录页面,如果有则放行 HttpSession session = request.getSession(); if(session.getAttribute("username") != null){ return true; } request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } }
springmvc.xml中配置拦截器
<mvc:interceptors> <mvc:interceptor> <!-- 拦截请求的路径 要拦截所有必需配置成/** --> <mvc:mapping path="/**"/> <!-- 指定拦截器的位置 --> <bean class="cn.guojie.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
<form action="${pageContext.request.contextPath }/login/submit" method="post"> <table> <tr><td>用户名:<input name="username" type="text"/></td></tr> <tr><td>密码:<input name="pwd" type="password"/></td></tr> <tr><td><input value="登录" type="submit"/></td></tr> </table> </form>
只要有请求就会被先被拦截器拦截
请求,地址栏访问 ----> 要直接是/login/login地址访问就拦截放行跳到login.jsp,要地址不是 ----> 拦截器拦截判断登录路径和登录信息,都不满足,转发到登录页面login.jsp去登录 --->login.jsp页面submit提交时重定向再次发请求,被拦截器拦截判断登录路径,满足放行 ----> ${pageContext.request.contextPath }/login/submit请求到LoginController的submit方法中提交 ----> 重定向到商品列表页,发送请求再次被拦截器拦截,登录路径不满足,但登录信息有,放行
web.xml中
*.action:所有action后缀都拦截
/:拦截除JSP外所有,可以不写action
/*:拦截所有