springMVC学习(二)

文章详细阐述了SpringMVC中请求转发与重定向的不同方式,包括String、ModelAndView及ServletAPI的使用。还介绍了视图解析器的配置以及模型数据在不同作用域(request、session、application)中的传递。此外,讨论了RESTful风格的请求处理和@ResponseBody、@RequestBody注解在处理HTTP响应和请求体数据中的作用。
摘要由CSDN通过智能技术生成
(一)请求转发与重定向
  • 在SpringMVC的控制单元中,方法定义返回类型可以有多种。理论上是返回任意类型都对。包括void。
  • 忽略AJAX请求处理方式,请求转发和响应重定向对应的控制单元返回值类型包括:
  • 1. String - 返回的字符串,默认就是结果视图的地址。
    
  •    默认是请求转发。实际上,类似指定请求转发方案  forward:/path
    
  •    响应重定向: redirect:/path
    
  • 2. ModelAndView - 使用Model传递请求作用域数据,使用view传递结果视图地址。
    
  •    默认的结果视图地址就是请求转发,可以使用类似的功能语法 forward:/path 提供viewName,
    
  •    可以使用redirect:/path 提供重定向viewName。
    
  •    如果使用ModelAndView实现重定向,那么其中的model请求作用域数据,会自动转换成同名请求参数传递
    
  • 3. void - 使用ServletAPI实现请求转发和重定向。
    
  • 如果结果路径是其他的控制器RequestMapping地址,编程方式和普通视图返回处理一致。
  • 注意:如果返回的路径使用forward:或redirect:开头,路径和前缀一定要紧挨在一起。
  • 注意:所有的返回结果地址,建议都使用 ‘/’ 开头,都是相对应服务器的根开始寻址。准确。
    @RequestMapping("/servletRedirect")
    public void servletRedirect(HttpServletResponse response) throws IOException, ServletException{
        System.out.println("servletRedirect运行");
        response.sendRedirect("/index.jsp");
    }

    @RequestMapping("/servletForward")
    public void servletForward(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("servletForward运行");
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }

    @RequestMapping("/redirectMV")
    public ModelAndView redirectMV(){
        System.out.println("redirectMV运行");
        ModelAndView mv = new ModelAndView();
        mv.addObject("t", "测试重定向");
        mv.setViewName("redirect:/index.jsp");
        return mv;
    }

    @RequestMapping("/forwardMV")
    public ModelAndView forwardMV(){
        System.out.println("forwardMV运行");
        ModelAndView mv = new ModelAndView();
        mv.addObject("t", "测试数据传递");
        mv.setViewName("forward:/index.jsp");
        // mv.setViewName("/index.jsp");
        return mv;
    }
//返回结果为字符串
    @RequestMapping("/testRedirect")
    public String testRedirect(){
        System.out.println("testRedirect方法运行");
        return "redirect:/index.jsp";
    }

    @RequestMapping("/testForward")
    public String testForward(){
        System.out.println("testForward方法运行");
        return "forward:/index.jsp";
        // return "/index.jsp";
    }
(二)视图解析器
 配置视图解析器,这是一个bean对象。这个对象,由springmvc框架自动使用。是基于byType找对象的。
     可以不配置唯一标记。 id或name
     这就是默认的视图解析器。在sping-mvc.xml中配置
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 可以配置属性 -->
        <!-- 默认情况下,返回的字符串viewName,会自动拼接前后缀,组成完整的视图地址。 -->
        <!-- 统一的前缀 -->
        <!--<property name="prefix" value="/WEB-INF/jsp/"></property>-->
        <!-- 统一的后缀 -->
        <!--<property name="suffix" value=".jsp"></property>-->
    </bean>
  • Java:
    @RequestMapping("/test1")
    public String direct(){
        System.out.println("隐私中的direct运行");
        return "index";
    }
(三)变量作用域

1.request

  • 1. 使用ModelAndView : 使用Model传递数据
    
  • 2. 使用控制单元(方法)参数,Map类型的参数。 直接在方法参数表中定义Map类型的参数,put的键值对,自动传递到请求作用域
    
  • 3. 使用控制单元参数, Model类型的参数。 在方法参数表中定义Model类型的参数,调用add系列方法,自动传递到请求作用域。
    
  • 4. 使用ServletAPI, request.setAttribute。 Servlet原生API。
    
   @RequestMapping("/reqScopeModel")
   public String reqScoopeModel(Model model){
       System.out.println(model.getClass().getName());
       // 相当于request.setAttribute
       model.addAttribute("msg", "使用Model传递的数据");
       Map<String, Object> attrs = new HashMap<>();
       attrs.put("testMap", "测试使用addAllAttributes传递数据");
       // 迭代map,使用map的key作为attribute名字,map的value作为attribute值,调用addAttribute方法。
       model.addAllAttributes(attrs);
       return "/bjsxt/getReqScope";
   }

   @RequestMapping("/reqScopeMap")
   public String reqScopeMap(Map<String, Object> map){
       System.out.println(map.getClass().getName());
       map.put("msg", "使用Map集合传递的数据");
       return "/index.jsp";
   }

   @RequestMapping("/reqScopeMV")
   public ModelAndView reqScopeMV(){
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg", "使用ModelAndView传递的数据");
       mv.setViewName("/index.jsp");
       return mv;
   }
  • 取值
  • 1. 使用ServletAPI, request.getAttribute
    
  • 2. 使用注解,让SpringMVC帮助从请求作用域中获取attribute。
    
//    @RequestAttribute获取request中的值
    @RequestMapping("/testAtt")
    public String test(@RequestAttribute("msg") String msg,@RequestAttribute("hash") String h){
        System.out.println("请求作用域: msg = " + msg);
        System.out.println("请求作用域: testMap = " + h);
        return "/index.jsp";
    }
  • 2.会话变量作用域: session
  • 设置:
  •  1. 只能通过ServletAPI实现。
    
  • 取值:
  •  1. 使用ServletAPI获取, request.getSession().getAttribute() 或者 session.getAttribute()
    
  •  2. 使用方法参数+注解实现。
    
    @RequestMapping("/getSessionScope")
    public String getSessionScope(@SessionAttribute("session1") String session1,
                                  @SessionAttribute String session2){
        System.out.println("session1 = " + session1);
        System.out.println("session2 = " + session2);
        return "/index.jsp";
    }

    @RequestMapping("/sessionScope")
    public String sessionScope(HttpServletRequest request, HttpSession session){
        request.getSession().setAttribute("session1", "基于request.getSession.setAttribute传递");
        session.setAttribute("session2", "基于session.setAttribute传递");

        return "/index.jsp";
    }
  • 应用上下文变量作用域: application。避免全局直接使用,存在的安全隐患。 不推荐使用。
  • 设置:
  •  1. 使用ServletAPI实现。 request.getServletContext().setAttribute()
    
  • 取值:
  •  1. 使用ServletAPI实现。 request.getServletContext().getAttribute()
    
    @RequestMapping("/getAppScope")
    public String getAppScope(HttpServletRequest request){
        System.out.println("application app = " + request.getServletContext().getAttribute("app"));
        return "/index.jsp";
    }

    @RequestMapping("/applicationScope")
    public String applicationScope(HttpServletRequest request){
        request.getServletContext().setAttribute("app", "通过application传递的数据");
        return "/index.jsp";
    }
(四)配置静态资源放行
<!-- 配置静态资源放行
     配置例外,当请求地址是什么的时候,前端控制器DispatcherServlet不处理。直接放行请求到静态资源。
     mapping - 请求地址的映射规则, 使用通配符配置。 如 /pic/*
     location - 静态资源在哪一个目录中,寻址方案是,把mapping的*统配部分作为目录中的资源查找。
     如: 请求地址是 /pic/1.webp, mapping配置的*对应 1.webp。在location文件夹/pic/中找 1.webp

     静态资源放行,相当于请求转发。实际上,这个标签,相当于配置了一个controller。
     mapping就是controller中方法的@RequestMapping地址。 location相当于方法的返回结果拼接方案。

     ant映射方式: 使用同配置配置映射地址。可用位置: 所有springmvc的映射地址配置。
     如: 静态资源放行、 @RequestMapping
       单级别统配: *。 如: /pic/* , 代表目录/pic/中的任意资源。不包括子目录。
            /pic/* 对应 /pic/1.webp  /pic/abc  /pic/123  /pic/000
       多级别统配: /**。 如: /pic/**, 代表目录/pic/中的任意资源,包括子目录。
           /pic/** 对应 /pic/1.webp  /pic/1/1.webp  /pic/abc  /pic/a/b/c
 -->
    <mvc:resources mapping="/pic/**" location="/pic/"></mvc:resources>
    <mvc:resources mapping="/pic2/**" location="/WEB-INF/pic/"></mvc:resources>
(五)restful风格
  • DELETE 请求,没有请求体
  • 传参方式必须:
    1. ?传参
    1. restFul风格参数。 /path/参数
  •  springMVC的控制器,处理restful风格参数,必须给方法参数加注解。
    
  •  PathVariable(value="requestMapping中的{}内的变量名,如果和参数名一样可以省略属性")
    
    @DeleteMapping("/user/{suibian}")
    @ResponseBody
    public String removeUser(@PathVariable("suibian") Integer id){
        System.out.println("删除: " + id);
        return "删除成功";
    }
  • PUT 请求, 没有请求体

  • 传参方式

    1. ?传参
    1. restFul风格参数。 /path/参数1/参数2/参数3/…/参数n
  • restFul风格参数 不能处理 ‘/’。

    @PutMapping("/user/{id}/{name}")
    @ResponseBody
    public String modifyUser(@PathVariable Integer id, @PathVariable String name){
        System.out.println("修改:id = " + id + " , name = " + name);
        return "修改";
    }
(六)@ResponseBody
  • @ResponseBody - 控制器方法的返回值,通过响应输出流,输出到客户端浏览器。
  • 根据控制单元(方法)返回值类型,决定输出到客户端时,ContentType响应头类型的。
  • 当方法增加@ResponseBody注解的时候,@RequestMapping注解的属性produces可以用于设置响应头contentType
  • @ResponseBody 注解,在向客户端输出数据的时候,是需要转换方法返回值 到 客户端(浏览器)可识别的内容的。
  • 这个转换是基于SpringMVC框架中的HttpMessageConverter Http协议消息转换器实现的。
  • 需要项目中导入依赖 jackson 相关依赖。 才能让HttpMessageConverter 生效。
  • SpringMVC 认为,正常情况下, 响应输出流向客户端输出基本数据类型的可能性很低。没有提供对应的HttpMessageConverter实现
  • 使用的是通用实现。可以删除produces属性。做尝试。不同的springmvc版本jar包,提供不能的服务能力。
  • 所谓通用实现,根据导入的jackson相关依赖不同,使用不同的实现。
  • jackson-databind - 通用实现是json转换。
    1. 基本数据类型 - 默认设置响应头 content-type = application/json。 把方法返回值直接输出到客户端浏览器。
    1. String类型 - 默认设置响应头 content-type=text/html;charset=ISO-8859-1。
  • 认为返回的字符串,是一个可显示的HTML视图。把字符串返回值直接输出到客户端。
    
  • 可以通过Mapping的属性produces设置响应头,规避乱码问题。
    
    1. 其他引用类型 - 设置响应头 content-type=application/json。
  • 使用HttpMessageConverter中的json转换器,把Java对象,转换成JSON格式字符串。
    
  • 转换过程,基于property。property的名字是json中的属性名,property的value是json中的属性值。
    
  • 注意:返回的引用类型中,getter和setter必须成对出现,除getClass外。
    
  • 异常情况: 当处理此数据的反序列化时,会抛出异常。 把JSON格式字符串,转换成Java类型对象时。
    
  •           且jackson-databind版本过低的时候,可能在java转换json格式字符串的时候,就会抛出异常。
    
    1. 集合类型 - 设置响应头 content-type=application/json。
  • Map - 效果等用引用类型对象。
    
  • List和Set - 是增加了JSON数组的引用类型对象。也就是[{},{}]
    
  • 总结:
    1. 返回String, content-type = text/html;charset=ISO-8859-1
    1. 返回其他, content-type=application/json
    @GetMapping("/testSet")
//    @ResponseBody
    public Set<User> testSet(){
    //public @ResponseBody Set<User> testSet(){
        System.out.println("返回Set集合");
        Set<User> set = new HashSet<>();
        set.add(new User(1, "吕布"));
        set.add(new User(2, "貂蝉"));
        return set;
    }

    @GetMapping("/testList")
//    @ResponseBody
    public List<User> testList(){
        System.out.println("返回List集合");
        List<User> list = new ArrayList<>();
        list.add(new User(1, "关羽"));
        list.add(new User(2, "张飞"));
        list.add(new User(3, "刘备"));
        return list;
    }

    @GetMapping("/testMap")
//    @ResponseBody
    public Map<String, Object> testMap(){
        System.out.println("返回Map集合");
        Map<String, Object> map = new HashMap<>();
        map.put("name", "李四");
        map.put("gender", "男");

        return map;
    }

    @GetMapping("/testRef")
//    @ResponseBody
    public User testReturnReference(){
        System.out.println("返回引用类型对象");
        User user = new User();
        user.setId(10);
        user.setName("张三");
        return user;
    }
(七)@requestBody

@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List<Map>等类型。

既然是转换为请求体数据,所以不能是GET类型请求(GET没有请求体),多用在POST类型的请求中。

@RequestBody注解在单体架构项目使用的不是特别多。主要用在分布式项目中多个项目之间传递数据或一些开发平台中(例如微信开发平台接口返回XML数据)

如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML

    @PostMapping("/listUser")
    public List<User> listUser(@RequestBody List<User> userList){
        System.out.println(userList);
        return userList;
    }

    @PostMapping("/listMap")
    public List<Map<String, Object>> listMap(@RequestBody List<Map<String, Object>> listMap){
        System.out.println(listMap);
        return listMap;
    }

    @PostMapping("/map")
    public Map<String, Object> map(@RequestBody Map<String, Object> map){
        System.out.println(map);
        return map;
    }

    /**
     * 请求参数中,包含一个用户对象。
     *
     * 使用要求:
     *  1. 请求方式必须POST
     *  2. 请求content-type必须是application/json
     *  3. 请求参数使用请求体传递,必须是JSON格式字符串。
     *
     * RequestBody参数,使用JSON描述请求参数,理论上可以传递任何参数。
     * 如:自定义对象,List,数组,Map等。
     * 底层也是使用jackson-databind实现的。
     *
     * @param user
     * @return
     */
    @PostMapping("/testRequestBodyUser")
    public User testRequestBody1(@RequestBody User user){
        System.out.println(user);
        return user;
    }

其中前端请求代码:

    <script src="/js/jquery.js"></script>
    <script>
        /**
         * 当请求content-type=application/json时,
         * 使用ajax发起请求,一定要设置dataType(响应数据类型)。
         * 只有content-type和dataType同时设置,请求体参数,才会封装成json对象。
         * 否则都是 参数名=参数值&参数名=参数值 的字符串|表单域。
         *
         * 请求参数JSON对象,被AJAX处理后,转换成了名值对表单参数。
         * 使用JSON.stringify(JSON对象),手工转换对象成为JSON格式字符串。可以实现请求体传递JSON格式字符串。
         *
         * JSON.stringify(JSON对象) - 是把参数对象,转换成JSON格式字符串的方法。
         * JSON对象  {"id":1, "name":"admin"}
         * JSON格式字符串是   "{\"id\":1, \"name\":\"admin\"}"   '{"id":1, "name":"admin"}'
         *
         */
        $(function () {
            $("#btn").on('click', function () {
                var data = {"id":$("#id").val(), "name":$("#name").val()};
                $.ajax({
                    'url' : '/testRequestBodyUser',
                    'type' : 'post',
                    'contentType' : "application/json",
                    // 'data' : JSON.stringify(data),
                    'data' : data,
                    'success' : function (data) {
                        alert(data);
                    },
                    'dataType':'json' // 响应的
                });
            })

            $("#mapBtn").on('click', function () {
                var data = {"name":"张三", "gender" : "男"};
                $.ajax({
                    'url': '/map',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })

            $("#listMapBtn").on('click', function () {
                var data = [];
                data.push({"name":"李四", "gender":"男"});
                data.push({"name":"王五", "gender":"男"});
                $.ajax({
                    'url': '/listMap',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })

            $("#listUserBtn").on('click', function () {
                var data = [];
                data.push({"id":1, "name":"赵四"});
                data.push({"id":2, "name":"刘能"});
                $.ajax({
                    'url': '/listUser',
                    'type': 'post',
                    'data':JSON.stringify(data),
                    'contentType': 'application/json',
                    'dataType': 'json',
                    'success': function (data) {
                        alert(data);
                    }
                });
            })
        });
    </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值