JAVAWEB开发之SpringMVC详解(二)——高级开发、数据回显、参数绑定集合、图片上传、json交互、validation校验、异常处理、RESTful支持、拦截器
来源:互联网 发布:python数据分析怎么样 编辑:程序博客网 时间:2024/05/01 09:03
知识回顾
springmvc框架
用户请求url到DispatcherServlet前端控制器,相当于中央调度器,降低系统各组件之间的耦合度。
DispatcherServlet前端控制器通过HandlerMapping处理器映射器根据url找到Handler。
DispatcherServlet前端控制器通过HandlerAdapter处理器适配器执行Handler。
DispatcherServlet前端控制器得到Handler返回的ModelAndView通过视图解析器ViewResolver进行视图解析。
视图解析:将程序中写的逻辑视图名,转成真正的视图(springmvc通过view表示各个类型不同的视图)。DispatcherServlet前端控制器调用View的渲染方法进行视图渲染(将ModelAndView中的model放到request域)
重点:SpringMVC的注解开发,企业中常用springmvc的注解开发
使用专门注解处理器映射器(RequestMappingHandlerMapping)和处理器适配器(RequestMappingHandlerAdapter)
注意:使用<mvc:annotation-driven/>可以替代上边的注解处理器映射器和注解处理器适配器的配置。
在Handler(controller)中定义很多的方法,一个方法通过@RequestMapping对url进行映射。
方法返回值:ModelAndView、String(jsp的逻辑视图名)、void(通过response将数据输出成json)。
方法输入参数(形参):springmvc需要将请求的key/value(串,id=001&id=002)、解析绑定到Handler(controller)中方法的形参上。
springmvc默认支持多类型的参数绑定。
默认支持的类型:HttpServletRequest、HttpServletResponse、HttpSession、Model(用于将数据填充到request域)。
@RequestParam注解:用于绑定单个请求参数,常用于简单类型参数(Integer、String、Float......)绑定。
不使用@ReuestParam要求请求参数的名称和方法形参名一致方可进行绑定。
对于简单类型参数中的日期型,建议使用自定义参数绑定,对日期类型数据个性化定义日期的格式。
自定义参数绑定:建议使用Converter进行参数绑定。
还可以绑定pojo、包装的pojo.
高级知识清单
注解开发:
数据回显:表单提交错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显。
集合类型(String[]、List<>、Map)的参数绑定
springmvc上传图片(重点)
json数据交互(提交json数据、响应json数据)(重点)
Validation(springmvc使用校验方式: 使用Hibernate Validator(和Hibernate的ORM没有任何关系) )
拦截器(用于权限控制)
数据回显
需求
表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的数据在页面上显示。
对简单类型的数据回显
对商品修改数据回显:
注意在进入修改页面的controller方法中和提交修改商品信息方法model.addAttribute方法设置key一致。
修改商品显示方法:
@RequestMapping(value="/editItems",method={RequestMethod.GET}) public String editItems(Model model,Integer id) throws Exception{ // 将id传递到页面 model.addAttribute("id",id); // 调用Service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); //将模型数据传到jsp model.addAttribute("item",itemsCustom); //return "editItem_2"; return "editItem"; }
修改页面
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" ><input type="hidden" name="id" value="${id }"/>修改商品信息:<table width="100%" border=1><tr><td>商品名称</td><td><input type="text" name="name" value="${item.name }"/></td></tr> ......修改商品提交方法
@RequestMapping("/editItemSubmit")public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 进行数据回显model.addAttribute("id", id);// 提交后回到修改页面return "editItem";}提交后查看网页源码
可以发现只有id回显到了修改页面
其过程原理是这样的:
第一次点击修改跳转到修改页面,从request域取值为name的id的属性赋值。
点击提交后将name的值(请求参数)提交到提交方法中为形参id赋值。
然后在提交方法内将id存入request域,再跳转到编辑页面,进行回显。
pojo数据类型回显
回显方法1:
使用model.addAttribute方法进行数据回显
@RequestMapping("/editItemSubmit")public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 进行数据回显model.addAttribute("id", id);model.addAttribute("item",itemsCustom);// 提交后回到修改页面return "editItem";}过程就是:提交后表单数据传入到提交方法的形参id和itemsCustom中。
然后将形参存入到request域,属性命名保持与编辑页面取数据的名称一致。
跳转到编辑页面即可从request取值 进行回显。
回显方法2:
使用@ModelAttribute, 作用于将请求pojo数据放到Model中回显到页面
public String editItemSubmit(Model model,Integer id, @ModelAttribute(value="item")ItemsCustom itemsCustom) throws Exception {在@ModelAttribute方法指定的名称就是要填充到Model中的key,在页面上就要通过key取数据。
@ModelAttribute将方法返回值传到页面
需求:商品类别信息在商品信息页面展示// 单独将商品类型的方法提出来,将方法返回值填充到request,在页面显示@ModelAttribute("itemsType")public Map<String, String> getItemsType() throws Exception{HashMap<String, String> itemsType = new HashMap<String, String>();itemsType.put("001", "数码");itemsType.put("002", "服装");return itemsType;}页面
商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select>使用@ModelAttribute将公用的取数据的方法返回值传递到页面,不用在controller的每一个方法中通过Model将数据传到页面。
参数绑定集合类型
绑定数组
需求:在商品查询列表页面,用户选择要删除的商品,批量删除商品。
在controller方法中如何将批量提交的数据绑定成数组类型。
页面定义
<td><input type="checkbox" name="delete_id" value="${item.id }"/></td>
controller方法定义
// 删除商品@RequestMapping("/deleteItems")public String deleteItems(Integer[] delete_id) throws Exception{System.out.println(delete_id);return "success";}运行结果:
绑定List<Object>
先进入批量修改商品页面,填写信息,点击提交
页面定义
<c:forEach items="${itemsList }" var="item" varStatus="s"><tr><td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td><td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"/></td>......</tr>注释:name值中的itemList
itemsList:controller方法形参包装类型中list的属性名
itemsList[0]或itemsList[0]......[]中是序号,从0开始
itemsList[index].name就是controller方法形参包装类型中list中pojo的属性名。
Controller方法定义
使用包装类型接收页面批量提交的数据,绑定成list
public class ItemsQueryVo {// 商品信息private ItemsCustom itemsCustom;// 定义一个Listprivate List<ItemsCustom> itemsList; ......}
使用包装类型作为形参 接收参数
// 批量修改商品提交@RequestMapping("/editItemsListSubmit") public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){return "success";}
springmvc和struts的区别
SpringMVC是通过方法的形参接受参数,在使用时可以以单例方式使用,建议使用单例。
Struts是通过成员变量接收参数,在使用时必须以多例方式使用。
SpringMVC是基于方法开发,Struts是基于类开发。
SpringMVC将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
SpringMVC开发是以方法为单位进行开发,方法更贴近Service(业务方法)。
经过实际测试,发现Struts标签解析速度比较慢,建议在实际开发中使用jstl。
商品图片上传
需求
在商品修改页面,增加图片上传的功能。
操作流程:
用户进入商品修改页面
上传图片
点击提交(提交的是图片和商品的信息)
再次进入修改页面,图片在商品修改页面展示
图片存储的问题
切记:不要将图片上传到工程目录,不方便进行工程维护。
实际电商项目中使用专门的图片服务器(比如Apache、Tomcat)
在Tomcat中进行虚拟目录的设置
设置方法如下:
在Tomcat下,找到conf文件下的server.xml,打开,在<Host>和</host>之间加上如下代码,然后就部署完成,重启服务器,浏 览器可以访问:
<Context path="/虚拟目录名" docBase="目标目录位置" debug="0" reloadable="true" ></Context>
虚拟目录名:浏览器访问的地址
目标目录位置:项目所在目录的绝对路径
reloadable="true" :服务器配置动态加载
测试:拖进目录中一张图片,启动服务器进行测试:
注意:图片目录中尽量进行目录分级存储,提高IO的访问速度。
配置图片上传解析器
springmvc使用commons-fileupload进行图片上传
commons-fileupload对应的springmvc的图片上传解析器:org.springframework.web.multipart.commons.CommonsMultipartResolver
在springmvc.xml中配置如下:
<!-- 文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置上传文件的最大尺寸为5MB --><property name="maxUploadSize"><value>5242880</value></property></bean>加入commons-fileupload的jar包
编写上传图片的页面
表单属性的enctype要设置为multipart/form-data
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data">添加图片上传组件<input type="file" name=""> 其中name的属性值要与控制器中接收图片的方法中的形参一致。
<td>商品图片</td><td><c:if test="${item.pic != null }"><img src="/pic/${item.pic}" width="100px" height="100px"/><br/></c:if><input type="file" name="pictureFile"/></td>
编写控制器方法
@RequestMapping("/editItemSubmit")// public String editItemSubmit(Integer id,ItemsCustom// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{public String editItemSubmit(Model model,Integer id, @ModelAttribute(value="item")ItemsCustom itemsCustom,// 上传图片MultipartFile pictureFile) throws Exception {// 进行数据回显model.addAttribute("id", id);//model.addAttribute("item",itemsCustom);//进行图片上传if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {// 图片上传成功后,将图片的地址写到数据库String filePath = "/Users/liuxun/Desktop/pictures";// 上传文件原始名称String originFileName = pictureFile.getOriginalFilename();// 新的图片的名称String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));// 新文件File file = new File(filePath+File.separator+newFileName);// 将内存中的文件写入磁盘pictureFile.transferTo(file);// 图片上传成功,将图片地址写入数据库itemsCustom.setPic(newFileName);}// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 提交后回到修改页面return "editItem";// 请求重定向//return "redirect:queryItems.action";// 转发// return "forward:queryItems.action";}提交后页面效果如下
json数据交互
需求
json数据格式是比较简单和容易理解的,json数据格式常用于远程接口传输,http传输json数据,非常方便页面进行 提交/请求结果解析,对json数据的解析。SpringMVC解析json加入json解析包
springmvc默认采用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包如下所示:
在处理器适配器中注入MappingJacksonHttpMessageConverter
让处理器适配器支持json数据解析,需要注入MappingJacksonHttpMessageConverter
<!-- 注解适配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 --> <property name="webBindingInitializer" ref="customBinder"/> <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 --> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> </list> </property> </bean>需要将支持json数据解析的消息转换器注入到注解适配器中。
@RequestBody和@RespoonseBody
@RequestBody:将请求的json数据转成Java对象
@ResponseBody:将Java对象转成json数据输出。
请求json响应json
controller方法
//请求的json响应json,请求商品信息,商品信息使用json格式,输出商品信息@RequestMapping("/requestJson")public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{return itemsCustom;}页面(需要引入jQuery)
// 请求json响应jsonfunction requestJson(){$.ajax({url:"${pageContext.request.contextPath}/requestJson.action",type:"post",contentType:"application/json;charset=utf-8",//请求json数据 使用json表示商品信息data:'{"name":"手机","price":1999}',success:function(data){alert(data.name);}});}测试:
请求key/value响应json
controller方法
@RequestMapping("/responseJson")public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{return itemsCustom;}页面
// 请求key/value响应jsonfunction responseJson(){$.ajax({url:"${pageContext.request.contextPath}/responseJson.action",type:"post",//contentType:"application/json;charset=utf-8",//请求key/value数据 使用地址拼接表示商品信息data:"name=手机&price=1999",success:function(data){alert(data.name);}});}测试:
总结:
如果前端处理没有特殊要求建议使用第二种,请求key/value, 返回json, 方便客户端解析请求结果。validation校验
对前台的校验大多数通过js在页面校验,这种方法比较简单,如果出于安全性考虑,还需要在后台进行校验。SpringMVC使用JSR-303(JavaEE规范第一部分)校验规范,SpringMVC使用的是Hibernate Validator
加入Hibernate Validator的jar
在处理器适配器中配置校验器
在注解适配器中注入自定义的webBinder
<!-- 注解适配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 --> <property name="webBindingInitializer" ref="customBinder"/> <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 --> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> </list> </property> </bean>在webBinder中注入自定义的校验器
<!-- 自定义webBinder --><bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"><!-- 使用converter进行参数转换 --><property name="conversionService" ref="conversionService"/><!-- 配置Validator --><property name="validator" ref="validator"/></bean>配置自定义校验器
<!-- 校验器 --><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器 --><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/><!-- 指定校验所使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties--><property name="validationMessageSource" ref="messageSource"/></bean><!-- 校验错误信息配置文件 --><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><!-- 资源文件名 --><property name="basenames"><list><value>classpath:CustomValidationMessages</value></list></property><!-- 资源文件编码方式 --><property name="fileEncodings" value="utf-8"/><!-- 对资源文件内容缓存时间,单位秒 --><property name="cacheSeconds" value="120"/></bean>
创建CustomValidationMessages
在classpath下新建CustomValidationMessages.properties校验规则
需求:编辑商品信息提交时校验,商品生产日期不能为空,商品名称在1-30个字符之间
public class Items { private Integer id; //商品名称的长度限制在1到30个字符 @Size(min=1,max=30,message="{items.name.length.error}") private String name; private Float price; private String pic; //请输入商品生产日期 @NotNull(message="{items.createtime.is.notnull}") private Date createtime; ......}
捕获错误
需要修改controller方法,在需要校验的形参pojo前边加@Validated, 在pojo的后边加上参数BindingResult用来接收错误信息@RequestMapping("/editItemSubmit") //注意:每个校验pojo的前边必须加@Validated, 每个校验的pojo后边必须加BindingResult接收错误信息public String editItemSubmit(Model model,Integer id, @Validated @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {错误信息输出
// 输出错误信息// 如果参数绑定时有错误if (bindingResult.hasErrors()) {// 获取错误List<ObjectError> errors = bindingResult.getAllErrors();// 准备在页面输出errors,页面使用jstl遍历model.addAttribute("errors",errors);for (ObjectError error : errors) {// 输出错误信息System.out.println(error.getDefaultMessage());}// 如果校验错误,回到商品修改页面return "editItem";}
在页面上展示错误
<!-- 错误信息 --><div style="color: red;"> <c:forEach items="${errors }" var="error">${error.defaultMessage }<br></c:forEach></div>效果如下:
分组校验
需求:针对不同的controller方法通过分组校验达到个性化校验的目的,修改商品的修改功能,只校验商品的生产日期不能为空。第一步:创建分组接口
package liuxun.ssm.controller.validation;/** * 校验分组: 用于商品修改的校验分组 * @author liuxun * */public interface ValidGroup1 {//接口不定义方法,就是只标识 哪些校验规则属于ValidGroup1分组}第二步:定义校验规则属于哪个分组
//请输入商品生产日期 //通过groups指定此校验属于哪个分组,可以指定多个分组 之间用逗号隔开groups={ValidGroup1.class,ValidGroup2.class} @NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class}) private Date createtime;第三步:在controller方法中使用定义的校验分组
public String editItemSubmit(Model model,Integer id, @Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {运行如下:
统一异常处理
需求
一般项目中都需要做异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。
系统中的异常类型:
包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码中手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常。
统一异常处理解决方案
自定义异常
针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类。
CustomException:用于测试。
public class CustomException extends Exception {//异常信息private String message;public CustomException(String message) {super();this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
异常处理
要在一个统一异常处理的类中要处理系统抛出的所有异常,根据异常类型处理。统一异常处理类是什么?
前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler的过程中,如果遇到异常进行异常处理
在系统中自定义统一的异常处理器,写系统自己的异常处理代码。
自定义异常处理器类
统一异常处理器实现HandlerExceptionResolver接口
public class CustomExceptionResolver implements HandlerExceptionResolver {//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {// 统一处理异常代码// ......return null;}}
配置统一异常处理类
<!-- 定义统一异常处理器类 --><bean class="liuxun.ssm.exception.CustomExceptionResolver"/>
异常处理逻辑
根据不同的异常类型进行异常处理。系统自定义的异常类是CustomException,在Controller方法中、Service方法中手动抛出此类异常。
针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示。
针对非CustomException异常,对这类异常重新构造成一个CustomException,异常信息为"未知错误", 此类错误需要在系统测试阶段进行排除。
在统一异常处理器CustomExceptionResolver中实现上边逻辑。
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {//输出异常ex.printStackTrace();//统一处理异常代码//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面进行展示//异常信息String message = null;CustomException customException = null;//如果ex是自定义异常信息,直接取出异常信息if (ex instanceof CustomException) {customException = (CustomException) ex;} else {//针对非CustomException异常,对这类重新构成一个CustomException,异常信息为"未知错误"customException = new CustomException("未知错误");}//错误信息message = customException.getMessage();request.setAttribute("message", message);try {//转向到错误页面request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);} catch (ServletException | IOException e) {e.printStackTrace();}return new ModelAndView();}展示错误信息页面
<body>${message }</body>
测试抛出异常由统一异常处理器捕获
可以在controller方法、service方法、dao实现类中抛出异常,要求dao、service、controller遇到异常全部向上抛出异常,方法向上抛出异常throws Exceptionpublic ItemsCustom findItemsById(int id) throws Exception {Items items = itemsMapper.selectByPrimaryKey(id);//如果查询的商品信息为空,抛出系统自定义异常if (items == null) {throw new CustomException("修改商品信息不存在");}// 在这里随着需求的变量,需要查询商品的其他相关信息,返回到controllerItemsCustom itemsCustom = new ItemsCustom();// 将items的属性拷贝到itemsCustomBeanUtils.copyProperties(items, itemsCustom);return itemsCustom;}测试修改方法 参数改成一个不存在的值
流程图解如下:
RESTful支持
什么是RESTful
一、REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。如果一个架构符合REST原则,就称它为RESTful架构。
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。
二、资源(Resources)
REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
三、表现层(Representation)
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
四、状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
五、综述
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
六、误区
RESTful架构有一些典型的设计误区。
最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:
POST /accounts/1/transfer/500/to/2
正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
另一个设计误区,就是在URI中加入版本号:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
url的RESTful实现
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....RESTful的url是简洁的:http:// localhost:8080/items/editItems/1
参数通过url传递,rest接口返回json数据
需求
根据id查看商品信息,商品信息查看的连接使用RESTful方式实现,商品信息以json方式返回。
第一步更改DispatcherServlet配置
在web.xml中添加如下配置
<!-- restful配置 --><servlet><servlet-name>springmvc_rest</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc_rest</servlet-name><!-- rest方式配置为/ --><url-pattern>/</url-pattern></servlet-mapping>
第二步参数通过url传递
//根据id查看商品信息rest接口 //@RequestMapping中指定restful方式的url参数,参数要使用{}包起来 //@PathVariable将url中的{}包起来的参数和形参进行绑定 @RequestMapping("/viewItems/{id}") public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{ //调用Service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); return itemsCustom; }
第三步设置静态资源解析
当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就会报错需要在springmvc.xml中通过设置对静态资源解析
<!-- 静态资源解析 --><mvc:resources location="/js/" mapping="/js/**"/><mvc:resources location="/img/" mapping="/img/**"/>
访问/js/**的url从工程下/js/下解析
测试结果:注意:使用<mvc:annotation-driven/>可以替代注解映射器和注解适配器的配置,而且原先需要向注解适配器中注入webBinder(包含validator校验器以及converter参数转换器或者属性编辑器) 使用<mvc:annotation-driven/>替代后不用webBinder 可以直接设置到<mvc:annotation-driven/>标签的节点属性中,如下所示:
springmvc拦截器
拦截器的通常场合
用户请求到DIspatcherServlet中,DispatcherServlet调用HandlerMapping查找Handler,HandlerMapping返回一个拦截器的链,springmvc的拦截器是通过HandlerMapping发起的。
在企业开发中,使用拦截器实现用户认证(用户登陆后进行身份校验拦截器),用户权限拦截。
springmvc拦截器方法
自定义拦截器,需要实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor{//在执行handler之前执行的//用于用户认证校验、用户权限校验@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("HandlerInterceptor1...preHandle");//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}//在执行handler返回modelAndView之前执行//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {System.out.println("HandlerInterceptor1...postHandle");}//执行handler之后执行此方法//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长//实现系统,统一日志记录@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)throws Exception {System.out.println("HandlerInterceptor1...afterCompletion");}}
测试拦截器
定义两个拦截器
配置拦截器
配置全局拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping映射器。
在springmvc.xml中配置:
<!-- 拦截器 --><mvc:interceptors><!-- 多个拦截器,顺序执行 --><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean></mvc:interceptor></mvc:interceptors>
测试1:1号和2号都放行
测试结果HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
执行preHandler是顺序执行的
执行postHandler、afterHandler是倒序执行的
测试2:1号放行,2号不放行
HandlerInterceptor1...preHandleHandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
如果当前拦截器的preHandler不放行,postHandler、afterHandler都不放行。
拦截器链中只要有一个拦截器不放行,所有拦截器的postHandler都不会执行,controller就不能执行完成。
测试3:1号和2号都不放行
测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor1...preHandle
总结:
只有前边的拦截器preHandler方法放行,下边的拦截器的preHandler才会执行。
日志拦截器或异常拦截器要求
将日志拦截器或异常拦截器放在拦截器链中的第一个位置,且preHandler方法放行。
拦截器应用(用户认证拦截器)
需求
用户访问系统的资源(url),如果用户没有进行身份认证,进行拦截,系统跳转登录页面,如果用户已经认证通过,用户可以访问系统资源。
用户登录及退出功能开发
@Controllerpublic class LoginController {//用户登录提交方法@RequestMapping("/login")public String login(HttpSession session,String usercode,String password) throws Exception{//调用service校验用户账号和密码的正确性//...//如果Service校验通过,将用户身份记录到sessionsession.setAttribute("usercode", usercode);//重定向到商品查询页面return "redirect:/items/queryItems.action";}//用户退出@RequestMapping("/logout")public String logout(HttpSession session) throws Exception{//session失效session.invalidate();//重定向到商品查询页面return "redirect:/items/queryItems.action";}}
用户身份认证拦校验截器
拦截器实现思路:http请求URL,如果URL是公开地址(不需要认证即可访问的URL) 放行,如果用户在Session中存在 放行,如果用户在Session中不存在,跳转到登录页面。自定义登录拦截器
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //得到请求的urlString url = request.getRequestURI();//判断是否是公开地址//实际开发中需要将公开地址配置在配置文件中//...if (url.indexOf("login.action")>=0) {//如果是公开地址 则放行return true;}//判断用户身份在Session中是否存在HttpSession session = request.getSession();String usercode = (String) session.getAttribute("usercode");//如果用户身份在session中存在则放行if (usercode!=null) {return true;}//执行到这里拦截,跳转到登录页面,用户进行身份认证request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}在springmvc.xml中配置拦截器
<mvc:interceptors> <mvc:interceptor> <!-- /**可以拦截多层路径 --> <mvc:mapping path="/**"/> <bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor></mvc:interceptors>效果如下:
实例源代码如下
源码已经上传GitHub https://github.com/LX1993728/springmvc_mybatis_1工程目录结构如下:
web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID" version="2.5"><display-name>springmvc_mybatis_1</display-name><!-- 配置Spring容器监听器 --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 前端控制器 --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- 可以配置/ ,此工程 所有请求全部由springmvc解析,此种方式可以实现 RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析 可以配置*.do或*.action,所有请求的url扩展名为.do或.action由springmvc解析,此种方法常用 不可以/*,如果配置/*,返回jsp也由springmvc解析,这是不对的。 --><url-pattern>*.action</url-pattern></servlet-mapping><!-- restful配置 --><servlet><servlet-name>springmvc_rest</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加载springmvc配置 --><init-param><param-name>contextConfigLocation</param-name><!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml" 即:springmvc-serlvet.xml --><param-value>classpath:spring/springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc_rest</servlet-name><!-- rest方式配置为/ --><url-pattern>/</url-pattern></servlet-mapping><!-- post乱码处理 --><filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app>SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- 定义别名 --><typeAliases> <!-- 批量别名定义 指定包路径,自动扫描包下边的pojo, 定义别名,别名默认为类名(首字母小写或大写) --><package name="liuxun.ssm.po" /></typeAliases><!-- 由于使用Spring和mybatis整合的mapper扫描器,这里可以不用配置了 <mappers><package name="liuxun.ssm.mapper"/></mappers>--></configuration>applicationContext-dao.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 加载配置文件 --><context:property-placeholder location="classpath:db.properties" /><!-- 数据库连接池 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxActive" value="10"/> <property name="maxIdle" value="5"/></bean><!-- SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"/> <!-- mybatis配置文件 --> <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/> </bean> <!-- MapperScannerConfigurer: mapper扫描器,将包下边的mapper接口自动创建代理对象, 自动创建到Spring容器中,bean的id是mapper的类名(首字母小写) --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 配置扫描包的路径 如果要扫描多个包,中间使用半角逗号隔开 要求mapper.xml和mapper.java同名且在同一目录 --> <property name="basePackage" value="liuxun.ssm.mapper"/> <!-- 使用SqlSessionFactoryBeanName --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean></beans>applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 商品管理的Service --> <bean id="itemsService" class="liuxun.ssm.service.impl.ItemsServiceImpl"></bean></beans>applicationContext-transaction.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 使用声明式事务,可以有效规范代码 --><!-- 事务管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="save*" propagation="REQUIRED" /><tx:method name="insert*" propagation="REQUIRED" /><tx:method name="update*" propagation="REQUIRED" /><tx:method name="delete*" propagation="REQUIRED" /><tx:method name="find*" propagation="SUPPORTS" read-only="true" /><tx:method name="select*" propagation="SUPPORTS" read-only="true" /><tx:method name="get*" propagation="SUPPORTS" read-only="true" /></tx:attributes></tx:advice><!-- aop --><aop:config><aop:advisor advice-ref="txAdvice" pointcut="execution(* liuxun.ssm.service.impl.*.*(..))" /></aop:config></beans>springmvc.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 使用spring组件扫描 --><context:component-scan base-package="liuxun.ssm.controller" /><!-- 静态资源解析 --><mvc:resources location="/js/" mapping="/js/**"/><mvc:resources location="/img/" mapping="/img/**"/><!-- 通过使用mvc的annotation-driven 可以替代下边的处理器映射器和适配器 --><!-- <mvc:annotation-driven conversion-service="conversionService" > </mvc:annotation-driven> --><!-- 注解处理器映射器 --><beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> <!-- 注解适配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器 --> <property name="webBindingInitializer" ref="customBinder"/> <!-- 加入json数据的消息转换器 MappingJacksonHttpMessageConverter依赖的Jackson包 --> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> </list> </property> </bean> <!-- 配置视图解析器 要求将jstl的包加到classpath --><!-- ViewResolver --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- 自定义webBinder --><bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"><!-- 配置Validator --><property name="validator" ref="validator"/><!-- 使用converter进行参数转换 --><property name="conversionService" ref="conversionService"/><!-- propertyEditorRegistrars用于属性编辑器 --><!-- <property name="propertyEditorRegistrars"><list><ref bean="customPropertyEditor"/></list></property>--></bean><!-- 注册属性编辑器 --><bean id="customPropertyEditor" class="liuxun.ssm.controller.propertyeditor.CustomPropertyEditor"/><!-- 注册转换器 --><bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="converters"><list><bean class="liuxun.ssm.controller.converter.CustomDateConverter"/><bean class="liuxun.ssm.controller.converter.StringTrimConverter"/></list></property></bean><!-- 拦截器 --><mvc:interceptors><!-- 多个拦截器,顺序执行 --> <!-- <mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean></mvc:interceptor> --> <mvc:interceptor> <!-- /**可以拦截多层路径 --> <mvc:mapping path="/**"/> <bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor></mvc:interceptors><!-- 文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置上传文件的最大尺寸为5MB --><property name="maxUploadSize"><value>5242880</value></property></bean><!-- 定义统一异常处理器类 --><bean class="liuxun.ssm.exception.CustomExceptionResolver"/><!-- 校验器 --><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器 --><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/><!-- 指定校验所使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties--><property name="validationMessageSource" ref="messageSource"/></bean><!-- 校验错误信息配置文件 --><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><!-- 资源文件名 --><property name="basenames"><list><value>classpath:CustomValidationMessages</value></list></property><!-- 资源文件编码方式 --><property name="fileEncodings" value="utf-8"/><!-- 对资源文件内容缓存时间,单位秒 --><property name="cacheSeconds" value="120"/></bean></beans>CustomValidationMessages.properties
db.properties
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatisjdbc.username=rootjdbc.password=rootlog4j.properties
# Global logging configuration\uff0c\u5efa\u8bae\u5f00\u53d1\u73af\u5883\u4e2d\u8981\u7528debuglog4j.rootLogger=DEBUG, stdout# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%nItemsController.java
package liuxun.ssm.controller;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.propertyeditors.CustomDateEditor;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.validation.BindingResult;import org.springframework.validation.ObjectError;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.servlet.ModelAndView;import liuxun.ssm.controller.validation.ValidGroup1;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;import liuxun.ssm.service.ItemsService;/** * 商品管理 * * @author liuxun * */@Controller// 定义url的根路径,访问时 根路径+方法的url@RequestMapping("/items")public class ItemsController {// 注入Service@Autowiredprivate ItemsService itemsService;// 单独将商品类型的方法提出来,将方法返回值填充到request,在页面显示@ModelAttribute("itemsType")public Map<String, String> getItemsType() throws Exception{HashMap<String, String> itemsType = new HashMap<String, String>();itemsType.put("001", "数码");itemsType.put("002", "服装");return itemsType;}// 查询商品信息方法@RequestMapping("/queryItems")public ModelAndView queryItems(HttpServletRequest request) throws Exception {// 调用Service查询商品列表List<ItemsCustom> itemsList = itemsService.findItemsList(null);ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("itemsList", itemsList);// 指定逻辑视图名modelAndView.setViewName("itemsList");return modelAndView;}// 批量修改商品查询@RequestMapping("/editItemsList")public ModelAndView editItemsList(HttpServletRequest request) throws Exception {// 调用Service查询商品列表List<ItemsCustom> itemsList = itemsService.findItemsList(null);ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("itemsList", itemsList);// 指定逻辑视图名modelAndView.setViewName("editItemsList");return modelAndView;}// 批量修改商品提交@RequestMapping("/editItemsListSubmit") public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){return "success";}// 商品修改页面// 使用method=RequestMethod.GET限制使用get方法/* @RequestMapping(value="/editItems",method={RequestMethod.GET}) public ModelAndView editItems() throws Exception{ ModelAndView modelAndView = new ModelAndView(); // 调用Service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(1); //将模型数据传到jsp modelAndView.addObject("item", itemsCustom); // 指定逻辑视图名 modelAndView.setViewName("editItem"); return modelAndView; } */// 方法返回字符串,字符串就是逻辑视图名,Model作用就是将数据填充到request域,在页面展示 @RequestMapping(value="/editItems",method={RequestMethod.GET}) public String editItems(Model model,Integer id) throws Exception{ // 将id传递到页面 model.addAttribute("id",id); // 调用Service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); //将模型数据传到jsp model.addAttribute("item",itemsCustom); //return "editItem_2"; return "editItem"; } //根据id查看商品信息rest接口 //@RequestMapping中指定restful方式的url参数,参数要使用{}包起来 //@PathVariable将url中的{}包起来的参数和形参进行绑定 @RequestMapping("/viewItems/{id}") public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{ //调用Service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); return itemsCustom; }// 方法返回void/*@RequestMapping(value = "/editItems", method = { RequestMethod.GET })public void editItems(HttpServletRequest request, HttpServletResponse response,// @RequestParam(value="item_id",required=false,defaultValue="1")Integer idInteger id) throws Exception {// 调用Service查询商品信息ItemsCustom itemsCustom = itemsService.findItemsById(id);request.setAttribute("item", itemsCustom);// 注意如果使用request转向页面,这里指定页面的完整路径request.getRequestDispatcher("/WEB-INF/jsp/editItem.jsp").forward(request, response);}*/// 商品修改提交// itemsQueryVo是包装类型的pojo@RequestMapping("/editItemSubmit")// public String editItemSubmit(Integer id,ItemsCustom// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{ //注意:每个校验pojo的前边必须加@Validated, 每个校验的pojo后边必须加BindingResult接收错误信息public String editItemSubmit(Model model,Integer id, @Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,BindingResult bindingResult,// 上传图片MultipartFile pictureFile) throws Exception {// 输出错误信息// 如果参数绑定时有错误if (bindingResult.hasErrors()) {// 获取错误List<ObjectError> errors = bindingResult.getAllErrors();// 准备在页面输出errors,页面使用jstl遍历model.addAttribute("errors",errors);for (ObjectError error : errors) {// 输出错误信息System.out.println(error.getDefaultMessage());}// 如果校验错误,回到商品修改页面return "editItem";}// 进行数据回显model.addAttribute("id", id);//model.addAttribute("item",itemsCustom);//进行图片上传if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {// 图片上传成功后,将图片的地址写到数据库String filePath = "/Users/liuxun/Desktop/pictures";// 上传文件原始名称String originFileName = pictureFile.getOriginalFilename();// 新的图片的名称String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));// 新文件File file = new File(filePath+File.separator+newFileName);// 将内存中的文件写入磁盘pictureFile.transferTo(file);// 图片上传成功,将图片地址写入数据库itemsCustom.setPic(newFileName);}// 调用Service接口更新商品信息itemsService.updateItems(id, itemsCustom);// 提交后回到修改页面return "editItem";// 请求重定向//return "redirect:queryItems.action";// 转发// return "forward:queryItems.action";}// 删除商品@RequestMapping("/deleteItems")public String deleteItems(Integer[] delete_id) throws Exception{System.out.println(delete_id);return "success";}//自定义属性编辑器/*@InitBinderpublic void initBinder(WebDataBinder binder) throws Exception {// Date.class必须是与controler方法形参pojo属性一致的date类型,这里是java.util.Datebinder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));}*/}JsonTest.java
package liuxun.ssm.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import liuxun.ssm.po.ItemsCustom;/** * json测试 * @author liuxun * */@Controllerpublic class JsonTest {//请求的json响应json,请求商品信息,商品信息使用json格式,输出商品信息@RequestMapping("/requestJson")public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{return itemsCustom;}@RequestMapping("/responseJson")public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{return itemsCustom;}}LoginController.java
package liuxun.ssm.controller;import javax.servlet.http.HttpSession;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;/** * 登录和退出 * @author liuxun * */@Controllerpublic class LoginController {//用户登录提交方法@RequestMapping("/login")public String login(HttpSession session,String usercode,String password) throws Exception{//调用service校验用户账号和密码的正确性//...//如果Service校验通过,将用户身份记录到sessionsession.setAttribute("usercode", usercode);//重定向到商品查询页面return "redirect:/items/queryItems.action";}//用户退出@RequestMapping("/logout")public String logout(HttpSession session) throws Exception{//session失效session.invalidate();//重定向到商品查询页面return "redirect:/items/queryItems.action";}}CustomDateConverter.java
package liuxun.ssm.controller.converter;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import org.springframework.core.convert.converter.Converter;/** * 自定义日期转换器 * @author liuxun * */public class CustomDateConverter implements Converter<String, Date> {@Overridepublic Date convert(String source) {if (source!=null&&source.trim().length()>0) {// 进行日期转换try {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);} catch (ParseException e) {e.printStackTrace();}}return null;}}StringTrimConverter.java
package liuxun.ssm.controller.converter;import org.springframework.core.convert.converter.Converter;/** * 自定义去除字符串前后空格的转换器 * @author liuxun * */public class StringTrimConverter implements Converter<String, String>{@Overridepublic String convert(String source) {//去掉字符串两边空格,如果去除后为空设置为nullif (source!=null) {source = source.trim();if (source.equals("")) {return null;}}return source;}}LoginInterceptor.java
package liuxun.ssm.controller.interceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;/** * 测试拦截器1 * @author liuxun * */public class LoginInterceptor implements HandlerInterceptor{//在执行handler之前执行的//用于用户认证校验、用户权限校验@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //得到请求的urlString url = request.getRequestURI();//判断是否是公开地址//实际开发中需要将公开地址配置在配置文件中//...if (url.indexOf("login.action")>=0) {//如果是公开地址 则放行return true;}//判断用户身份在Session中是否存在HttpSession session = request.getSession();String usercode = (String) session.getAttribute("usercode");//如果用户身份在session中存在则放行if (usercode!=null) {return true;}//执行到这里拦截,跳转到登录页面,用户进行身份认证request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);//如果返回false表示拦截器不继续执行handler,如果返回true表示放行return false;}//在执行handler返回modelAndView之前执行//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现 从modelAndView入手@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {System.out.println("HandlerInterceptor2...postHandle");}//执行handler之后执行此方法//作为系统统一异常处理,进行方法执行性能监控,在preHandler中设置一个时间点 在afterCompletion设置一个时间点 二者时间差就是执行时长//实现系统,统一日志记录@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)throws Exception {System.out.println("HandlerInterceptor2...afterCompletion");}}CustomPropertyEditor.java
package liuxun.ssm.controller.propertyeditor;import java.text.SimpleDateFormat;import java.util.Date;import org.springframework.beans.PropertyEditorRegistrar;import org.springframework.beans.PropertyEditorRegistry;import org.springframework.beans.propertyeditors.CustomDateEditor;/** * 自定义属性编辑器 * @author liuxun * */public class CustomPropertyEditor implements PropertyEditorRegistrar {public void registerCustomEditors(PropertyEditorRegistry binder) {binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));}}ValidGroup1.java
package liuxun.ssm.controller.validation;/** * 校验分组: 用于商品修改的校验分组 * @author liuxun * */public interface ValidGroup1 {//接口不定义方法,就是只标识 哪些校验规则属于ValidGroup1分组}CustomException.java
package liuxun.ssm.exception;public class CustomException extends Exception {//异常信息private String message;public CustomException(String message) {super();this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}CustomExceptionResolver.java
package liuxun.ssm.exception;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerExceptionResolver;import org.springframework.web.servlet.ModelAndView;public class CustomExceptionResolver implements HandlerExceptionResolver {//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {//输出异常ex.printStackTrace();//统一处理异常代码//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面进行展示//异常信息String message = null;CustomException customException = null;//如果ex是自定义异常信息,直接取出异常信息if (ex instanceof CustomException) {customException = (CustomException) ex;} else {//针对非CustomException异常,对这类重新构成一个CustomException,异常信息为"未知错误"customException = new CustomException("未知错误");}//错误信息message = customException.getMessage();request.setAttribute("message", message);try {//转向到错误页面request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);} catch (ServletException | IOException e) {e.printStackTrace();}return new ModelAndView();}}ItemsMapperCustom.java
package liuxun.ssm.mapper;import java.util.List;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;/** * 商品自定义Mapper * @author liuxun * */public interface ItemsMapperCustom { // 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;}ItemsMapperCustom.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="liuxun.ssm.mapper.ItemsMapperCustom"> <!-- 商品查询的sql片段 建议以单表为单位定义查询条件 建议将常用的查询条件都写出来 --> <sql id="query_items_where"> <if test="itemsCustom!=null"> <if test="itemsCustom.name!=null and itemsCustom.name.trim().length >0"> and name like '%${itemsCustom.name.trim()}%' </if> <if test="itemsCustom.id!=null"> and id = #{itemsCustom.id} </if> </if> </sql> <!-- 商品查询 parameterType:输入查询条件 --> <select id="findItemsList" parameterType="liuxun.ssm.po.ItemsQueryVo" resultType="liuxun.ssm.po.ItemsCustom"> SELECT * FROM items <where> <include refid="query_items_where"/> </where> </select> </mapper>ItemsCustom.java
package liuxun.ssm.po;/** * 商品信息的扩展类 * @author liuxun * */public class ItemsCustom extends Items {}ItemsQueryVo.java
/** * 商品的包装类 * @author liuxun * */public class ItemsQueryVo {// 商品信息private ItemsCustom itemsCustom;// 定义一个Listprivate List<ItemsCustom> itemsList;public ItemsCustom getItemsCustom() {return itemsCustom;}public void setItemsCustom(ItemsCustom itemsCustom) {this.itemsCustom = itemsCustom;}public List<ItemsCustom> getItemsList() {return itemsList;}public void setItemsList(List<ItemsCustom> itemsList) {this.itemsList = itemsList;}}修改自动生成的Items 添加校验
public class Items { private Integer id; //商品名称的长度限制在1到30个字符 @Size(min=1,max=30,message="{items.name.length.error}") private String name; private Float price; private String pic; //请输入商品生产日期 //通过groups指定此校验属于哪个分组,可以指定多个分组 之间用逗号隔开groups={ValidGroup1.class,ValidGroup2.class} @NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class}) private Date createtime; .... ....}ItemsService
package liuxun.ssm.service;import java.util.List;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;/** * 商品Service接口 * * @author liuxun * */public interface ItemsService {// 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;// 根据商品id查询商品信息public ItemsCustom findItemsById(int id) throws Exception;// 更新商品信息/** * 定义Service接口,遵循单一职责,将业务参数细化(不要使用包装类型,比如Map) * @param id 修改商品的id * @param itemsCustom 修改商品的信息 * @throws Exception */public void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception;}ItemsServiceImpl.java
package liuxun.ssm.service.impl;import java.util.List;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import liuxun.ssm.exception.CustomException;import liuxun.ssm.mapper.ItemsMapper;import liuxun.ssm.mapper.ItemsMapperCustom;import liuxun.ssm.po.Items;import liuxun.ssm.po.ItemsCustom;import liuxun.ssm.po.ItemsQueryVo;import liuxun.ssm.service.ItemsService;public class ItemsServiceImpl implements ItemsService {// 注入mapper@Autowiredprivate ItemsMapperCustom itemsMapperCustom;@Autowiredprivate ItemsMapper itemsMapper;// 商品查询列表public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {return itemsMapperCustom.findItemsList(itemsQueryVo);}public ItemsCustom findItemsById(int id) throws Exception {Items items = itemsMapper.selectByPrimaryKey(id);//如果查询的商品信息为空,抛出系统自定义异常if (items == null) {throw new CustomException("修改商品信息不存在");}// 在这里随着需求的变量,需要查询商品的其他相关信息,返回到controllerItemsCustom itemsCustom = new ItemsCustom();// 将items的属性拷贝到itemsCustomBeanUtils.copyProperties(items, itemsCustom);return itemsCustom;}public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {// 写业务代码// 对于关键业务数据的非空校验if (id == null) {// 抛出异常,提示调用接口的用户,id不能为空// ...}itemsMapper.updateByPrimaryKeySelective(itemsCustom);}}itemsList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>查询商品列表</title><script type="text/javascript">function deleteItems(){ // 将form的action指向删除商品的地址 document.itemsForm.action = "${pageContext.request.contextPath }/items/deleteItems.action"; // 进行form提交 document.itemsForm.submit();}</script></head><body> 当前用户:${usercode }<c:if test="${usercode != null}"><a href="${pageContext.request.contextPath}/logout.action">退出</a></c:if><form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post">查询条件:<table width="100%" border=1><tr><td>商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select></td></tr> <tr><td><input type="submit" value="查询"/><input type="button" value="批量删除" onclick="deleteItems()"/></td></tr></table>商品列表:<table width="100%" border=1><tr><td>选择</td><td>商品名称</td><td>商品价格</td><td>生产日期</td><td>商品描述</td><td>操作</td></tr><c:forEach items="${itemsList }" var="item"><tr> <td><input type="checkbox" name="delete_id" value="${item.id }"/></td><td>${item.name }</td><td>${item.price }</td><td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td><td>${item.detail }</td><td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td></tr></c:forEach></table></form></body></html>editItem.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>修改商品信息</title></head><body> <!-- 错误信息 --><div style="color: red;"><c:forEach items="${errors }" var="error">${error.defaultMessage }<br></c:forEach></div><form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data"><input type="hidden" name="id" value="${id }"/>修改商品信息:<table width="100%" border=1><tr><td>商品名称</td><td><input type="text" name="name" value="${item.name }"/></td></tr><tr><td>商品价格</td><td><input type="text" name="price" value="${item.price }"/></td></tr><tr><td>商品生产日期</td><td><input type="text" name="createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td></tr><tr><td>商品图片</td><td><c:if test="${item.pic != null }"><img src="/pic/${item.pic}" width="100px" height="100px"/><br/></c:if><input type="file" name="pictureFile"/></td></tr><tr><td>商品简介</td><td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"/></td></tr></table></form></body></html>editItemsList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>查询商品列表</title><script type="text/javascript">function updateItems(){ // 将form的action指向修改商品的地址 document.itemsForm.action = "${pageContext.request.contextPath }/items/editItemsListSubmit.action"; // 进行form提交 document.itemsForm.submit();}</script></head><body> <form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post" >查询条件:<table width="100%" border=1><tr><td>商品类别:<select> <c:forEach items="${itemsType }" var="item"><option value="${item.key }">${item.value }</option></c:forEach></select></td></tr> <tr><td><input type="submit" value="查询"/><input type="button" value="批量修改提交" onclick="updateItems()"/></td></tr></table>商品列表:<table width="100%" border=1><tr><td>商品名称</td><td>商品价格</td><td>生产日期</td><td>商品描述</td><td>操作</td></tr><c:forEach items="${itemsList }" var="item" varStatus="s"><tr><td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td><td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"/></td><td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td><td>${item.detail }</td><td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td></tr></c:forEach></table></form></body></html>jsontest.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>json测试</title><script type="text/javascript"src="${pageContext.request.contextPath}/js/jquery-1.4.4.min.js"></script><script type="text/javascript">// 请求json响应jsonfunction requestJson(){$.ajax({url:"${pageContext.request.contextPath}/requestJson.action",type:"post",contentType:"application/json;charset=utf-8",//请求json数据 使用json表示商品信息data:'{"name":"手机","price":1999}',success:function(data){alert(data.name);}});}// 请求key/value响应jsonfunction responseJson(){$.ajax({url:"${pageContext.request.contextPath}/responseJson.action",type:"post",//contentType:"application/json;charset=utf-8",//请求key/value数据 使用地址拼接表示商品信息data:"name=手机&price=1999",success:function(data){alert(data.name);}});}</script></head><body><input type="button" value="请求json响应json" onclick="requestJson()"/><input type="button" value="请求key/value响应json" onclick="responseJson()"/></body></html>
阅读全文
4 0
- JAVAWEB开发之SpringMVC详解(二)——高级开发、数据回显、参数绑定集合、图片上传、json交互、validation校验、异常处理、RESTful支持、拦截器
- 框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)
- SpringMvc入门到开发(五)之json数据交互,RESTful支持,拦截器【附详细截图】
- Springmvc学习笔记(4)之复杂参数类型绑定、校验、数据回显和异常处理
- springMVC学习笔记---day04-上传图片、JSON转对象、校验、全局异常、拦截器
- springMVC学习笔记--上传图片、JSON转对象、校验、全局异常、拦截器
- SpringMVC之json数据交互,RestFul风格与拦截器实现
- springmvc注解开发-高级之图片上传
- springmvc注解开发-高级之异常处理
- springmvc注解开发-高级之 拦截器
- springmvc注解开发-高级之Validation
- Springmvc关于图片上传,json数据,拦截器案例的详解
- Springmvc关于图片上传,json数据,拦截器案例的详解
- springmvc注解开发-validation校验
- springmvc注解开发-springmvc参数绑定-集合
- springmvc注解开发-validation校验-分组校验
- SpringMVC—Validation校验
- SpringMVC中Controller使用Validation的参数校验和统一处理参数异常
- VERO.ALPHACAM.V2017.R2
- 根据值来动态选中select option
- Java操作redis
- 欢迎使用CSDN-markdown编辑器
- UDP局域网广播实现CS房间列表
- JAVAWEB开发之SpringMVC详解(二)——高级开发、数据回显、参数绑定集合、图片上传、json交互、validation校验、异常处理、RESTful支持、拦截器
- leetcode week18
- Multiple Contexts have a path of "/xxxx"问题解决
- java提高篇(十四)-----关键字final
- HEEDS.MDO.2017.04
- Android 之 Material Design(三)—DrawerLayout+NavigationView+Toolbar(点击icon打开关闭侧滑菜单)
- DH的第一个博客
- 数据库连接池的配置和使用(大自然的搬运工)
- android6.0运行时权限