• Java框架之Spring MVC(二)


    一、Spring MVC 验证

    JSR 303 是ajvaEE6 中的一项子规范 ,叫 Bean Validation 用于对javaBean中的字段进行校验。

    官方的参考实现是: Hibernate Validator ,此实现和 Hibernate ORM 没有任何关系  //http://hibernate.org/validator

    Spring 3.x 也大力支持     JSR 303,使用的是  Hibernate Validator

    1) 导包

    4.3.1.Final 版本为例

    // jboss-logging-3.1.0.CR2.jar

    hibernate-validator-4.3.1.Final.jar

    jboss-logging-3.1.0.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="validatemessageSource"/> //不设置则默认为classpath下的 ValidationMessages.properties
    </bean>
                   
    <bean id="validatemessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
                   <property name="basename" value="classpath:validatemessages"/>  //配置验证信息资源文件的路径
                   <property name="fileEncodings" value="utf-8"/>  
                   <property name="cacheSeconds" value="120"/>   //缓存时间
    </bean>

    3) 创建校验信息用的资源文件,本例在类路径(src或config)下直接创建即可

    validatemessages.properties 内容

    name.not.empty=名字不能为空

    age.not.inrange=年龄要在合适的范围内

    email.not.correct=邮箱格式不正确

    email.not.empty=邮箱不能为空

    4) 在实体类上,添加校验

     public class UserInfo {
                        private  int id;
                        
                        @NotNull(message="用户名不能为 null")
                        @NotEmpty(message="{name.not.empty}")
                        @Pattern(regexp="13\d{9}",message="用户名是按手机验证的格式不对")
                        private String userName;   
                        
                        @Size(min=3,max=6,message="{password.not.inrange}")
                        private String password;
                        
                        @NotEmpty(message="{email.not.empty}")
                        @Email(message="{email.not.correct}")
                        private String note;
                        private String aihao;
                            
                //也可以将规则添在get 方法上    

    5) 控制器 

    //在添加用户这个请求提交上来的时候
    @RequestMapping(value="/addUser" ,method=RequestMethod.POST)
    public String addUser(Model model, @Valid UserInfo user,BindingResult bResult){
                            if(bResult.hasErrors()){
                                List<ObjectError> errorList=    bResult.getAllErrors();
                                for(ObjectError e:errorList){
                                    System.out.println(e.getDefaultMessage());
                                }
                                
                                model.addAttribute("errorList",errorList); //把错误信息传到前台
                                return "user_add";
                            }
                            
                            else{
                                return "success";
                            }
                        } 

    说明: BindingResult 是Errors的子类

    @Valid  和  BindingResult 的位置不要写反

    @Valid  写成了 @Validated 实测也可以

    6) 前端页面

    <c:forEach var="e" items="${errorList }">
                           ${e.defaultMessage}
                           </c:forEach>
                                       
                       <form action="${pageContext.request.contextPath }/addUser" method="post">
                       账号:    <input type="text" name="userName"  /> 
                       密码:  <input type="text" name="password"  /> 
                       备注:  <input type="text" name="note" /> 
                              <input type=submit value="提交" />
                       </form>

    例子 如何详细的显示出错信息

    新建页面 user_add_validate.jsp

     <%@ taglib uri="http://www.springframework.org/tags/form" prefix="x" %>
             <x:form method="post" action="${pageContext.request.contextPath }/addUser">
               账号:<x:input path="userName" /> <x:errors path="userName" /><br>
                密码:<x:input path="password" /> <x:errors path="password" /> <br>
               备注:<x:input path="note" /><x:errors path="note" /> <br>
               <input type=submit value="提交" />
        </x:form>

    问题:

    1) 如果直接访问  user_add_validate.jsp 出现错误 

    //java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?

    原因是Spring环境没初始化

    <context-param>    
    <param-name>contextConfigLocation</param-name>    
    <param-value>classpath:springmvc-servlet.xml</param-value>    
    </context-param>
    <listener>    
             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    2) 需要指定  modelAttribute="user" 这个属性

    <x:form method="post" action="${pageContext.request.contextPath }/addUser" modelAttribute="user"  >
    //上面也可以写成 commandName="user"

    3) 不能直接访问 user_add_validate.jsp 要这样:

    @RequestMapping(value="/addUser",method=RequestMethod.GET)
    public String addUser(@ModelAttribute("user") UserInfo user){   //@ModelAttribute("user") 必加
                    return "user_add_validate";
    }

    4) 添加用户的方法要如下

    @RequestMapping(value="/addUser" ,method=RequestMethod.POST)
                    public String addUser( @ModelAttribute("user") @Valid UserInfo user,BindingResult bResult){
                        if(bResult.hasErrors()){
                            return "user_add_validate";
                        }
                        else{
                            return "success";
                        }
                    } 

    附加:

    分组校验

    问题: 当一个pojo类被多个业务方法使用,但它的验证规则不同,如何处理?

    可以采用分组校验

    1)定义分组 (其实就是定义接口,空接口就可以,一个接口就是一个分组)

    public interface IVG1, IVG2

    2)在pojo类上指定校验属于哪个分组

    public class UserInfo(
    @NotEmpty(message="用户名不能为 null",groups={IVG1.class})
    
    @Size(min=3,max=6,message="{password.not.inrange}",groups={IVG1.class,IVG2.class})
    private String password;
                     ...
                }

    3)在业务方法上指定验证的时候使用哪个分组

    @RequestMapping(value="/addUser" ,method=RequestMethod.POST)
    public String addUser( @ModelAttribute("user") @Validated(value=IVG1.class) UserInfo user,BindingResult bResult){
                        if(bResult.hasErrors()){
                            return "user_add_validate";
                        }
                        else{
                            return "success";
                        }
                    } 
    //注意,用的是  @Validated 注解 不是 @Valid

    二、Spring MVC 数据的回显

    (其实上例就是带数据回显的)

    pojo类型的数据回显

    @RequestMapping(value="/addUser" ,method=RequestMethod.POST)
    public String addUser( @ModelAttribute("user") UserInfo user){
                          ....
                        } 

     页面的 from上也要加 modelAttribute 或 commondName

    <x:form action="${pageContext.request.contextPath }/test" method="post" modelAttribute="user">
    <x:input path="userName" />  //注意,要回显示的数据必须用 这样的标签
    <x:input path="password" />
    </x:form>

     对于简单类型

    用 model.addAttribute(...)  即可

    三、Spring MVC 异常处理

    SpringMVC 提供全局异常处理器进行统一的异常处理

    1) 在controller(Action)内部处理异常

    -- login.jsp

     <form action="user/login" method="post" >
                            账号:<input type="text" name="userName" /> 
                            密码:<input type="text" name="password" />
                            <input type="submit" />

    -- 自定义异常类 //不定义也可以

    public class MyException extends Exception{
                            private String msg;
                            public MyException(String msg){
                                super(msg);
                                this.msg=msg;
                            }
                            
                            public void writeLog(){
                                System.out.println("---异常信息"+this.msg+"已经记录-");
                            }
                        
                        }

    -- action 中的处理

    @RequestMapping(value="/login")
    public String login(HttpSession session,String userName,String password) throws MyException {
                            UserInfo user=new UserDao().getLoginUser(userName, password);
                            if(user==null){
                                throw new MyException("用户登录失败");  //如果登录失败,抛出异常
                                }
                                session.setAttribute("user", user);
                                return "success";
                            }
                            
                            @ExceptionHandler(value={MyException.class}) //这里是进行异常处理的地方
                            public String exectionProcessAAA(MyException e,HttpServletRequest request){
                            request.setAttribute("e_msg", e.getMessage());
                            e.writeLog();
                            return "error" ;  //全局的一个异常处理页面    
                        }

    2) 全局异常处理

    --在配置文件中加入配置

    <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">     
    <property name="exceptionMappings">     
        <props>     
        <prop key="cat.beans.MyException">error_process_1</prop>   //可以看到,可以为每种异常指定一个result视图
        <prop key="java.lang.Exception">error_process_2</prop>     
        <prop key="java.lang.Throwable">error_process_3</prop>     
        </props>     
    </property>     
    </bean>  

    在异常处理页面

    ${exception.message}  //这样即可取出异常信息

    四、Spring MVC 文件上传

    1) 单文件上传

    == 在配置文件中

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
                     <property name="defaultEncoding" value="UTF-8" />  //这里面的这些属性可以不用,一个都不写也不会出错
                     <property name="maxUploadSize" value="5400000" />
                     <property name="uploadTempDir" value="uploadTempDir" />
    </bean>

    == 页面

    <form method="post"  action="${pageContext.request.contextPath }/addUserWithImg"    enctype="multipart/form-data" >  
                       <input name="userName" /> 
                       <input name="password"/>  
                       <input name="note"/>  
                       <input type="file" name=upfile />  //注意,这个名字
                       <input type="submit" >
               </form> 

     == 控制层

    @RequestMapping(value="/addUserWithImg",method=RequestMethod.POST)
    public String addUserWithImg(UserInfo user,MultipartFile upfile /*注意这个名字要和表单元素同名*/ ,HttpServletRequest request ) 
    throws IllegalStateException, IOException{ System.out.println(upfile); System.out.println("contentType:"+upfile.getContentType()); //image/pjepg System.out.println("name:"+upfile.getName()); //photo System.out.println("getOriginalFilename:"+upfile.getOriginalFilename()); //zhangsan.jpg System.out.println("getSize:"+upfile.getSize()); new UserDao().addUser(user); String path=request.getServletContext().getRealPath("/upload_files"); File targetFile=new File(path, upfile.getOriginalFilename()); upfile.transferTo(targetFile); return "success"; }

    2) 多文件上传  

    <form method="post"  action="${pageContext.request.contextPath }/addUserWithMultImg"    enctype="multipart/form-data" >  
                           <input name="userName" /> 
                           <input name="password"/>  
                           <input name="note"/>  
                           
                           <input type="file" name=upfiles />
                           <input type="file" name=upfiles />
                           <input type="file" name=upfiles />
                           <input type="file" name=upfiles />
                           
                           <input type="submit" >
                   </form> 
    //注意要加上  @RequestParam
    public String ddUserWithMultImg(UserInfo user,@RequestParam("upfiles") MultipartFile [] upfiles ,HttpServletRequest request )
    throws IllegalStateException, IOException{ new UserDao().addUser(user); String path=request.getServletContext().getRealPath("/upload_files"); for(MultipartFile f:upfiles){ if(!f.isEmpty()){ File targetFile=new File(path, f.getOriginalFilename()); f.transferTo(targetFile); } } return "success"; }

    五、Spring MVC 资源文件的引入

    在配置文件中:

    <mvc:resources location="/images/" mapping="/images/**" />
    <mvc:resources location="/css/" mapping="css/**" />
    //也可以放在一个文件夹中,统一的引入,如
    <mvc:resources location="/resource/" mapping="resource/**" />

    六、Spring MVC 关于json数据处理

    1) 传统方式的 ajax访问和响应

    //请求发起页面  ajax_test.jsp
    $(function(){
    $("#btn1").click(function(){
    $.ajax({
            url:"testAjax",
            type:"post",
            data:{userName:'admin',password:'123'},
            async:false,
            cache:false,
            dataType:"json",
            success:function(data){
            //alert(data);
            alert(data.id);
            alert(data.note);
                                }
                            });
                        });
                    });
            
    @RequestMapping("/testAjax")
    public void testAjax(HttpServletRequest request,HttpServletResponse response) throws IOException{
                    String userName=request.getParameter("userName");
                    String password=request.getParameter("password");
                    System.out.println(userName+":"+password);
                    
                    response.setContentType("text/html;charset=utf-8");
                //response.getWriter().print("这是普通数据");
                    
                    String jsonStr="{"note":"这是备注","id":"90"}";
                    response.getWriter().print(jsonStr);
                }

    附注: 

    @RequestBody 用来处理application/json 类型的请求内容

    使用 @ResponseBody 的方式返回json 格式的数据

    要导入包 spring mvc 4.1.4

    jackson-annotations-2.4.4.jar、

    jackson-core-2.4.4.jar、

    jackson-databind-2.4.4.jar。

    附 3.1 用的是

    jackson-core-asl-1.9.11.jar 

    jackson-mapper-asl-1.9.11.jar

    //例一 从服务端查询出一个userInfo对象返回
                 //服务端
                    @RequestMapping("/testAjax2") @ResponseBody  //这里用 @ResponseBody 注解声明返回json数据
                    public UserInfo testAjax2(UserInfo u) {
                        UserInfo user=new UserDao().getLoginUser(u.getUserName(), u.getPassword());
                        return user;
                    }
                 //客户端
                     $.ajax({
                        url:"testAjax2",
                        type:"post",
                        data:{userName:'aaa',password:'aaa'},  //在服务端,用的是pojo类型的对象接收的参数
                        async:false,
                        cache:false,
                        dataType:"json",
                           success:function(data){
                            alert(data.id);
                            alert(data.note);
                        }
                    });
    //例二 从服务端查询一组对象返回
                    @RequestMapping("/testAjax3") @ResponseBody
                    public List<UserInfo> testAjax2() {
                        List<UserInfo> userList=new UserDao().getAllUser();
                        return userList;
                    }
                                            
                  $("#btn3").click(function(){
                            $.ajax({
                                url:"testAjax3",
                                type:"post",
                
                                async:false,
                                cache:false,
                                dataType:"json",
                                success:function(userList){ //从服务端返回的是一组对象
                                    $.each(userList,function(key,user){
                                        $("#div1").append(user.id +"-"+user.userName+"-"+user.password+"<br />");
                                    });
                                }
                            });
                        });

    七、Spring MVC中 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://localhost:8080/myweb/userAction_show.action?id=5  

    rest 风格 

    http://www.douban.com/photos/album/49432287/   这是豆瓣上的url rest风格

    查询用户

    http://localhost:8080/myweb/user/5 

    删除用户

    http://localhost:8080/myweb/user/5/delete

    //例子

    @RequestMapping("/get_user/{id}/{flag}") public String getUser(@PathVariable("id") int idAAA, @PathVariable("flag") String flagAAA){ System.out.println(idAAA +":"+flagAAA); return "success"; }

     请求的url

    http://localhost:8080/springmvc-03/get_user/10/search

    如果不传  PathVariable 指定的参数,会报404

    八、Spring MVC 拦截器

    SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。

    方式一: 用实现 HandlerInterceptor  接口的方式

    方式二: 继承抽象类HandlerInterceptorAdapter  (Spring 提供的类,它实现了 HandlerInterceptor)

    方式三: 实现WebRequestInterceptor 接口或继承实现了该接口的类

    例子:

    1) 拦截器声明

    public class MyInterceptor implements HandlerInterceptor {
                /*==在Controller 方法被调用之前执行,一般用来进行身份验证,授权等
                  == SpringMVC中的Interceptor 拦截器是链式的,可以同时存在多个Interceptor,并根据声明的前后顺序依次执行
                  == 而且所有的Interceptor中的 preHandle 方法都会在 Controller方法调用之前调用
                  == SpringMVC的这种Interceptor链式结构也是可以进行中断的,preHandle的返回值为false的时候整个请求就结束了 */
                public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                        Object arg2) throws Exception {
                    System.out.println("preHandle");
                    return true;
                }
            
                /* 一般在处理公用的模型数据,统一指定视图等操作的时候用
                 == 在preHandle方法返回值为true的时候才会执行。
                 == 它的执行时间是在是在Controller的方法调用之后,DispatcherServlet进行视图的渲染之前执行
                 == 可以对ModelAndView进行操作。
                 == 这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像          
                */
                public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                        Object arg2, ModelAndView modelAndView) throws Exception {
                    System.out.println("postHandle");    
                }
                
                /*
                 == 在preHandle方法返回值为true的时候才会执行。
                 == 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,  
                 == 这个方法的主要作用是用于清理资源的,统一的日志及异常处理等    */
                public void afterCompletion(HttpServletRequest arg0,
                        HttpServletResponse arg1, Object arg2, Exception arg3)
                        throws Exception {
                    System.out.println("afterCompletion");    
                }
            }

     2) 在配置文件中加入配置

    <mvc:interceptors>   
    //如果有 多个拦截器,顺序执行   
    <mvc:interceptor>   
    // 如果不配置或/*,将拦截所有的Controller
    <mvc:mapping path="/searchall_forupdate" /> path 配置拦的是请求的url
    //<mvc:mapping path="/**" /> 所有的url包扩子url 
    <bean class="cat.interceptor.MyInterceptor"></bean>   
    </mvc:interceptor>   
    
    </mvc:interceptors> 
    //例子,验证用户是否登录
            public class MySessionInterceptor implements HandlerInterceptor {
                        public void afterCompletion(HttpServletRequest arg0,
                                HttpServletResponse arg1, Object arg2, Exception arg3)
                                throws Exception {    }
                    
                    
                        public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                                Object handler, ModelAndView arg3) throws Exception {}
                    
                    
                        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                                Object obj) throws Exception {
                            String url=request.getRequestURI();
                            System.out.println(url);
                            if(url.contains("login")){  //如果用户发的是登录的请求,则直接放行
                                return true;
                            }
                            else{
                                UserInfo user=(UserInfo)request.getSession().getAttribute("user");
                                if(user!=null){
                                    return true;
                                }
                                else{
                                    request.setAttribute("msg", "请登录");
                                    request.getRequestDispatcher("login.jsp").forward(request, response);
                                    return false;
                                }
                            }
                        }

    上面的拦截器的配置文件

    <mvc:interceptors>
    <mvc:interceptor>
    <mvc:mapping path="/**" />
        <bean class="cat.interceptor.MyInterceptor"></bean>
    </mvc:interceptor>
                        
    <mvc:interceptor>
    <mvc:mapping path="/**" />
        <bean class="cat.interceptor.MySessionInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>  
  • 相关阅读:
    VTK 9.0.1 vtkContextDevice2D 问题
    VTK 中文
    VTK 剪切
    VTK Color Map
    VTK Camera
    VTK Light
    VTK Read Source Object
    VTK Procedural Source Object
    Qt 布局开发问题记录
    Grafana 系列 (7):圖表是否可以数据追踪 (drill down)?(转)
  • 原文地址:https://www.cnblogs.com/1693977889zz/p/8159067.html
Copyright © 2020-2023  润新知