1 验证
服务端验证
JSR 303 是java6中的一个子规范,叫 bean validator ,主要就是针对javabean中的字段进行校验
官方的参考实现是 hibernate validator ,springmvc 中用的就是 hibernate validator
1) 导包
这里以4.3.1.Final 为例
hibernate-validator-4.3.1.Final.jar
jboss-logging-3.1.0.CR2.jar
validation-api-1.0.0.GA.jar
2) 配置文件
<mvc:annotation-driven validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<property name="validationMessageSource" ref="validationMessageSource" />
</bean>
<bean id="validationMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource" >
<property name="basename" value="classpath:validationMessageSource" /> <!-- validationMessageSource是资源文件的名称,注意不要加扩展名(properties) -->
<property name="fileEncodings" value="utf-8" />
<property name="cacheSeconds" value="30" />
</bean>
3) 建立资源文件 validationMessageSource.properties 内容如下:
name.not.empty=名字不能为空
age.not.inrange=年龄超出范围
phone.not.correct=电话不正确
phone.not.empty=电话不能为空
email.not.empty=邮箱不能为空
4) 在实体类上,编写校验规则 ,可以把规则直接写在字段上,也可以写在get方法上
public class UserInfo {
private int id;
@NotNull(message="用户名不能为null")
@NotEmpty(message="{name.not.empty}")
private String userName;
private String password;
private String school;
@NotEmpty(message="{phone.not.empty}")
@Pattern(regexp="^1\\d{10}$",message="{phone.not.correct}")
private String phone;
@NotEmpty(message="{email.not.empty}")
@Email(message="邮箱格式不正确")
private String email;
@Range(max=200,min=1,message="{age.not.inrange}")
private int age;
...
}
5) Action 中
@RequestMapping(value="/user_add",method=RequestMethod.POST)
public String addUser( Model m,@ModelAttribute("user") @Validated UserInfo user,BindingResult bResult){
//证明有错误(校验失败)
if(bResult.hasErrors()){
List<ObjectError> errorList= bResult.getAllErrors();
for(ObjectError e:errorList){
System.out.println(e.getDefaultMessage()); //输出错误信息
}
m.addAttribute("errorList",errorList); //把错误信息传到前台
}
else{
dao.addUser(user);
}
return "user_add";
}
6) 前台
<c:forEach var="e" items="${errorList}">
${e.defaultMessage } <br />
</c:forEach>
<hr />
<form action="user_add" method="post">
用户信息
<hr>
账号:<input type="text" name="userName" value="${user.userName}" /> <br />
密码:<input type="text" name="password" value="${user.password}" /> <br />
学校:<input type="text" name="school" value="${user.school}" /> <br />
年龄:<input type="text" name="age" value="${user.age}" /> <br />
手机:<input type="text" name="phone" value="${user.phone}" /> <br />
邮箱:<input type="text" name="email" value="${user.email}" /> <br />
<input type="submit" value="提交" />
${msg }
</form>
//自由显示错误信息
<x:form action="user_add" method="post" modelAttribute="user" >
<x:errors path="*" /> <!-- 用 * 这样的方式是把所有的错误信息都输出 -->
<hr />
账号:<x:input path="userName" /> <x:errors path="userName" /> <br />
密码:<x:input path="password" /> <x:errors path="password" /> <br />
学校:<x:input path="school" /> <x:errors path="school" /> <br />
年龄:<x:input path="age" /> <x:errors path="age" /> <br />
手机:<x:input path="phone" /> <x:errors path="phone" /> <br />
邮箱:<x:input path="email" /> <x:errors path="email" /> <br />
<input type="submit" value="提交" />
</x:form>
测试:http://localhost:8080/springmvc-03/user_add
@RequestMapping(value="/user_add",method=RequestMethod.GET)
public String gotoAddUser(Model m, @ModelAttribute("user") UserInfo user){
return "user_add";
}
关于分组校验
问题: 当一个实体类被多个业务方法使用,且它们的校验规则不同时怎么办
可以指定校验分组 (分组就是一个接口)
定义两个接口 (多少都行 ) IVG1, IVG2 ,空接口就行 (IVG1:interface,validator,goroup)
在实体类上,指定验证属于哪个分组
// 电话属于第一组
@NotEmpty(message="{phone.not.empty}",groups={IVG1.class})
@Pattern(regexp="^1\\d{10}$",message="{phone.not.correct}",groups={IVG1.class}) //用正则表达式
private String phone;
// 邮箱属于第二组
@NotEmpty(message="{email.not.empty}",groups={IVG2.class})
@Email(message="邮箱格式不正确" ,groups={IVG2.class})
private String email;
在业务方法上,指定校验的时候要用哪个分组
public String addUser( Model m,@ModelAttribute("user") @Validated(value=IVG1.class) UserInfo user,BindingResult bResult)
2 资源文件的引入
在配置文件中 springmvc-config.xml :
<mvc:resources location="/css/" mapping=“css/" />
<mvc:resources location="/images/" mapping="images/” />
也可以把它们统一的放在同一个文件夹下 例如:放在 reources 文件夹下
<mvc:resources location="/reources/" mapping="reources/**" />
login.jsp:
<link rel="stylesheet" type="text/css" href="reources/css/style.css">
<p>我是登陆页面</p>
<img src="reources/image/3.png">
3 文件上传
1) 在配置文件中加入配置
//字节
//文件上传的临时目录
2)页面
<form action="admin_add" method="post" enctype="multipart/form-data" > //注意 enctype的取值必须 multipart/form-data
账号:<input type="text" name="adminName" />
密码:<input type="text" name="password" />
学校:<input type="text" name="school" />
头像:<input type="file" name="photo" />
<input type="submit" value="提交" />
</form>
3)控制层
public String addAdminWithPhoto(AdminInfo admin, MultipartFile photo,HttpServletRequest request) throws IllegalStateException, IOException{
System.out.println(admin);
System.out.println("上传文件的mime类型:"+ photo.getContentType());
System.out.println("上传的表单项的名称"+photo.getName());
System.out.println("上传的文件名:"+ photo.getOriginalFilename());
System.out.println("上传文件的大小"+photo.getSize());
String path= request.getServletContext().getRealPath("/upload_files");
String fileName=photo.getOriginalFilename();
File targetFile=new File(path,fileName);
photo.transferTo(targetFile);
//调用dao层,添加用户信息 略
return "admin_add";
}
附 多文件上传
@RequestMapping("/admin_add")
public String addAdminWithPhoto(AdminInfo admin, @RequestParam("photo") MultipartFile[] photo, HttpServletRequest request) throws Exception {
String path = request.getServletContext().getRealPath("/upload_files");
for (MultipartFile f : photo) {
if (!f.isEmpty()) {
String fileName = f.getOriginalFilename();
File targetFile = new File(path, fileName);
f.transferTo(targetFile);
}
}
//...调用dao层保存用户信息.略
request.setAttribute("msg", "文件上传成功");
return "admin_add";
}
<form action="admin_add" method="post" enctype="multipart/form-data" >
账号:<input type="text" name="adminName" />
密码:<input type="text" name="password" />
学校:<input type="text" name="school" />
头像:<input type="file" name="photo" />
头像:<input type="file" name="photo" />
头像:<input type="file" name="photo" />
头像:<input type="file" name="photo" />
<input type="submit" value="提交" />
${msg }
</form>
测试:若是文件名字相当则会覆盖。
4 Ajax 与 json 数据处理
例一
ajax_test.jsp:
<script type="text/javascript">
$(function(){
$("#btn1").click(function(){
$.ajax({
url:"ajax_getadmin",
type:"post",
async:false,
cache:false,
data:{adminId:$("#adminId").val()},
dataType:"json",
success:function(admin){
alert(admin.id);
alert(admin.adminName);
alert(admin.school);
}
});
});
});
</script>
<input type="text" id="adminId" >
<button id="btn">测试</button>
@RequestMapping("/ajax_getadmin")
public void test(HttpServletRequest request,HttpServletResponse response) throws IOException{
String adminId=request.getParameter("adminId");
System.out.println("adminId="+adminId);
AdminInfo admin=dao.getAdminById(Integer.parseInt(adminId));
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
//out.print("测试数据");
out.print( "{\"id\":\"90\",\"adminName\":\"admin\",\"school\":\"123\"}");
}
例二 使用 @ResponseBody 返回实体类对象
导包 (注意版本问题,spring 3 以前的需要换包,但是写法不换)
jackson-annotations-2.4.4.jar
jackson-core-2.4.4.jar
jackson-databind-2.4.4.jar
ajax_test.jsp:
$(function(){
$("#btn1").click(function(){
$.ajax({
url:"ajax_getadmin_new",
type:"post",
async:false,
cache:false,
data:{adminId:$("#adminId").val()},
dataType:"json",
success:function(admin){
alert(admin.id);
alert(admin.adminName);
alert(admin.note);
alert(admin.password);
}
});
});
});
AdminAction:
@RequestMapping("/ajax_getadmin_new")
@ResponseBody public AdminInfo getAdmin(int adminId){
AdminInfo admin=dao.getAdminById(adminId);
return admin;
}
例三 返回列表
<script type="text/javascript">
$(function(){
$("#btn1").click(function(){
$.ajax({
url:"ajax_get_alladmin",
type:"post",
async:false,
cache:false,
dataType:"json",
success:function(adminList){
$.each(adminList,function(key,admin){
var trStr="<tr><td>"+admin.id+"</td><td>"+admin.adminName+"</td><td>"+admin.password+"</td></tr>";
$("#table1").append(trStr);
});
}
});
});
});
</script>
<button id="btn1">查询全部</button>
<table border="1" width="400" id="table1">
</table>
@ResponseBody @RequestMapping("/ajax_get_alladmin")
public List<AdminInfo> getAllAdmin(){
return dao.getAllAdmin();
}
5 RESTful 风格
REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的
HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席
长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。
而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,
那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,
理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构
REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。
如果一个架构符合REST原则,就称它为RESTful架构
关于 Representational State Transfer (表现层状态转化) 的理解
1)REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。
网络上的一个具体信息,可以用一个URI(统一资源定位符)指向它
2) Representation 表现层
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的 因为这个后缀名表示格式,属于 "表现层"
范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,
这两个字段才是对"表现层"的描述。
3) State Transfer 状态转化
HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,
必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:
GET、POST、PUT、DELETE。它们分别对应四种基本操作:
GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
例如: 查看用户信息
传统的:
http://loclahost:8080/shop/UserAction?id=10&flag=update
RESTful风格的:
http://loclahost:8080/shop/user/10 ==> RESTful 风格
http://www.douban.com/photos/album/49432287/ 这是豆瓣网上的
例子
随便某个页面:
<a href="test_admin?id=10&flag=del" >删除用户</a>
上面的超链接,用rest风格
<a href="test_admin/10/delete" >删除用户</a>
AdminAction:
@RequestMapping("/test_admin/{id}/{flag}")
public String testAdmin(@PathVariable("id") int idAAA,@PathVariable("flag") String flagAAA){
System.out.println(idAAA);
System.out.println(flagAAA);
return "success";
}
测试:http://localhost:8080/admin-springmvc/test_admin/10/del 可以访问到success页面。
若是 http://localhost:8080/admin-springmvc/test_admin/10 就会报404 错误。