学习SpringMVC必知必会(3)~springmvc的请求和响应
阅读原文时间:2022年03月24日阅读:1

Controller方法该怎么返回、Controller数据该怎么进行共享

  • 返回void/ModelAndView/String

1、Controller方法返回void

    //返回void类型,此时可以把Controller方法当做Servlet使用【适合用来下载文件】
    @RequestMapping("/test1")
    public void test(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //请求转发
        request.getRequestDispatcher("/WEB-INF/views/welcome.jsp").forward(request, response);
        //设置共享数据
        request.setAttribute("msg", "hello");
        //输出json格式
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().println();
    }

2、【常用】返回ModelAndView

    //返回ModelAndView
    @RequestMapping("/test3")
    public ModelAndView test3() {
        ModelAndView mv = new ModelAndView();
        //设置视图名称
//        mv.setViewName("/WEB-INF/views/welcome.jsp");
        mv.setViewName("welcome");
        mv.addObject("msg", "返回ModelNAndView");//设置共享数据的key和value
        mv.addObject("叩丁狼教育");//设置共享数据的value,此时会把value类型的首字母作为key:string
        return mv;
    }

3、【常用】返回String类型(是逻辑视图名称),参数是Model类型(是共享数据)

    //返回string,逻辑视图名称,此时需要结合参数Model类型 一起使用
    @RequestMapping("/test4")
    public String test4(Model model) {
        //设置共享数据
        model.addAttribute("叩丁狼");
        model.addAttribute("msg", "教育");
        return "welcome";//设置跳转的视图
    }
  • 请求转发、URL重定向、URL重定向共享数据

1、请求转发

    //请求转发,浏览器地址栏不变,可以共享请求中的数据
    //原理:request.getRequestDispatcher("").forward(request, response);
    @RequestMapping("/test5")
    public String test5(Model model) {
        return "forward:/hello.jsp";//设置跳转的视图
    }

2、URL重定向

    //重定向,浏览器地址栏改变,不能共享请求中的数据
    //原理:request.sendRedirect("");
    @RequestMapping("/test6")
    public String test6(Model model) {
        return "redirect:/hello.jsp";//设置跳转的视图
    }

■ 请求资源的路径问题:[总结:访问资源的时候,都使用/开头]

  • 访问资源的时候前面带上/,表示绝对路径,根路径开始去寻找资源
  • 访问资源的时候签名不加/,表示相对路径,上一级上下文路径中去寻找资源

✿ 请求转发和URL重定向的选择:

请求转发/URL重定向

请求转发

URL重定向

地址栏改变?

不会

会改变

共享数据?

可以

不可以?

表单重复提交?

会发生

不会发生

  • 传统的方式,在url重定向的时候,因为是两次不同的请求,所以不能共享请求中的数据。

    在开发中,有时候真的需要重定向跳转后共享数据------------spring3.1开始,提供了Flash属性。
    只能是从Controller 重定向到 Controller,不能到jsp

3、URL重定向共享数据

    //重定向:从a跳转到b
    @RequestMapping("/a")
    public String a(RedirectAttributes ra) {
        ra.addAttribute("msg1", "a传递的数据");
        ra.addFlashAttribute("msg2", "msg2");
        return "redirect:/response/b";//设置跳转的视图
    }

    @RequestMapping("/b")
    public ModelAndView b(String msg1, @ModelAttribute("msg2") String msg2) {
        System.out.println("msg1:" + msg1);
        System.out.println("msg2:" + msg2);
        return null;
    }
  • #### 重定向共享数据的原理:更大的作用域--session

  • 处理器方法的请求参数该怎么携带、请求参数该怎么获取

1、request 和 response 参数

■ 情况一:为了操作Servlet API 对象,此时可以直接以参数形式传递,也可以直接使用DI注入。

@Controller
@RequestMapping("/request")
public class HandlerRequestController {

    @Autowired
    private ServletContext context;

    @RequestMapping("/test1")
    public void test(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws ServletException, IOException {
        System.out.println("request:" + request);
        System.out.println("response:" + response);
        System.out.println("session:" + session);
        System.out.println("ServletContext:" + this.context);

    }
}
  • request、response、session:建议使用参数
  • context 上下文:单例,建议使用属性,注入

因为Controller(Servlet) 是单例的,线程不安全,一般不用成员变量,除非要共享的数据,才作为成员变量。

2、简单类型参数

  • 处理简单类型的请求参数

■ 获取请求参数:【保证输入的参数和定义的形参名称一致】

    //获取请求参数:通过保证请求参数名称和Controller方法定义的形参(入参)同名即可
    @RequestMapping("/test2")
    public void test2(String username, int age)  {
        System.out.println(username);
        System.out.println(age);
    }

■ 获取请求参数:【输入的参数和定义的形参名称不一致】----注解@RequestParam

    //获取请求参数:若请求参数和请求参数名称和形参不同----注解@RequestParam
    @RequestMapping("/test3")
    public void test3(@RequestParam("name") String username, @RequestParam(value="age",required=false) Integer age)  {
        System.out.println("username:" +username);
        System.out.println("age:" + age);
    }

3、中文乱码处理

■ 在"全局配置"【web.xml】,添加上编码过滤器:

    <!-- (针对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>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

4、restfull风格传参

  • restfull 风格:软件架构风格,严格讲是一种编码风格,充分利用http协议本身的语义,从而提供一些设计原则和约束条件,主要是用来作为客户端和服务端的交互。

  • 简洁、有层次、容易实现缓存机制

    //restfull风格传递参数
    //PathVariable: 可以将url中占位符绑定到方法的形参中
    @RequestMapping("/r/{id}")
    public ModelAndView rest(@PathVariable("id") Integer id) {
        System.out.println("id:" + id);
        return null;
    }

5、数组和List类型参数

    //接收一个参数有很多值的情况
    //使用数组:可以直接接收  Long[] ids
    //使用List: 不可以直接接收,可以在对象中存在一个集合   List<Long> ids
    @RequestMapping("/batchDelete") //batchDelete?ids=10&ids=20&ids=30
    public void batchDelete(FormBean fb) {
        System.out.println(fb.getIds());
    }
★ 操作一个参数有多个值的情况,一般直接使用数组接收即可,或者使用javaBean对象来封装数据。

6、■ 获取请求参数:

JavaBean类型参数【将请求参数封装成一个对象

    //封装成一个对象
    @RequestMapping("/bean") //batchDelete?ids=10&ids=20&ids=30
    public void bean(User user) {
        System.out.println(user);
    }
  • 请求参数封装成javaBean对象,浏览器地址栏直接输入参数为对象的属性即可.

✿ 总结处理器方法参数处理:

1、直接把请求参数封装成javaBean对象
2、配置解决中文乱码的过滤器
3、简单类型参数----输入参数名和定义的形参不同-> @ResultParam
4、使用数组接收多个参数
5、restfull风格传参
  • 给共享的model数据设置key名,贴在形参上,也可以贴在方法上,

    针对复合类型参数,缺省情况下就会放到model中(共享), 缺省的key就是类型首字母小写

    • 使用注解 @ModelAttribute,起个别名

      //ModelAttribute注解:
      /*
      *1、给共享的model数据设置key名,贴在形参上,也可以贴在方法上

      • 针对复合类型(非简单类型)参数,缺省情况下就会放到model中(共享), 缺省的key就是类型首字母小写
      • 2、可以标注一个非请求处理的方法,被标注的方法,每次在请求处理方法之前都会优先被执行[存放多个请求需要共享的数据]
        */
        @RequestMapping("/test11")
        public String test1(@ModelAttribute("u") User user) {
        System.out.println(user);
        return "welcome";
        }

1、获取请求头 @RequestHeader

    @RequestMapping("/test1")
    public ModelAndView test1(@RequestHeader("User-Agent") String userAgent) {
        System.out.println("test1");
        System.out.println("User-Agent:" + userAgent);
        return null;
    }

2、获取Cookie @CookieValue

    @RequestMapping("/test1")
    public ModelAndView test1(@RequestHeader("User-Agent") String userAgent, @CookieValue("Webstorm-5895a979") String cName) {
        System.out.println("test1");
        System.out.println("User-Agent:" + userAgent);
        System.out.println("cName:" + cName);
        return null;
    }

3、操作HttpSession @SessionAttributes

  • 默认情况下模型数据是保存到 request 作用域的

    @Controller
    @RequestMapping("/other")
    @SessionAttributes("errMsg")
    public class OtherController {

    @RequestMapping("/test2")
    public String test2(Model model) {
        System.out.println("操作session");
        model.addAttribute("errMsg", "错误信息");
        return "redirect:/hello.jsp";
    }

    }

1、框架把 ServletRequest 对象和请求参数传递给 DataBinder

2、DataBinder 首先调用 Spring Web 环境中的 ConversionService 组件,进行数据类型转换和格式化等操作,将 ServletRequest 中的信息填充到形参对象中;

3、DataBinder 然后调用 Validator 组件对已经绑定了请求消息数据的形参对象进行数据合法性校验

4、DataBinder 最后输出数据绑定结果对象 BindingResult

  • BindingResult 包含了已完成数据绑定的形参对象和校验错误信息对象

    /*
     * 需要吧表单数据封装到多个对象中去,若各个对象有相同的属性时
     * 不知道该把哪一个参数封装到哪一个对象
     */
    @RequestMapping("/save")
    public ModelAndView save(Cat cat, Dog dog) {
        System.out.println("提交数据");
        System.out.println(cat);
        System.out.println(dog);
        return null;
    }
  • input.jsp 传递多个对象的参数时:

    <form action="/save" method="post">
        猫名:<input type="text" name="name" /><br/>
        猫年龄:<input type="text" name="age" /><br/>
        狗名:<input type="text" name="name" /><br/>
        狗年龄:<input type="text" name="age" /><br/>
        <input type="submit" value="提交">
    </form>

解决:

  • input.jsp:加上前缀做区分
    <form action="/save" method="post">
        猫名:<input type="text" name="cat.name" /><br/>
        猫年龄:<input type="text" name="cat.age" /><br/>
        狗名:<input type="text" name="dog.name" /><br/>
        狗年龄:<input type="text" name="dog.age" /><br/>
        <input type="submit" value="提交">
    </form>
  • 处理器Controller添加方法:

    //从参数--> 对象,封装规则需要我们来设置
    
    //自定义数据绑定注册,将请求参数转化成对应对象的属性
    @InitBinder("cat")
    public void initBinderCat(WebDataBinder binder) {
        //设置字段以什么做为前缀
        binder.setFieldDefaultPrefix("cat.");
    }
    
    @InitBinder("dog")
    public void initBinderDog(WebDataBinder binder) {
        //设置字段以什么做为前缀
        binder.setFieldDefaultPrefix("dog.");
    }

  • JSON处理

1、依赖:

  • jackson-annotations-2.12.2.jar
  • jackson-core-2.12.2.jar
  • jackson-databind-2.12.2.jar

2、处理json的注解 @ResponseBody @RestController @RequestBody

(1) @ResponseBody: 处理响应,把对象转化成json字符串

  • @ResponseBody 处理响应,把对象转化成json字符串

    • 贴到方法上:只会针对当前方法做json处理

    • 贴到类上:会对当前类中所有方法做json处理

      //把单个对象/Map转化成json格式
      @RequestMapping("/test1")
      @ResponseBody
      public User test1() {
      User u = new User();
      u.setUsername("shan");
      u.setAge(18);
      return u;
      }

      //把多个对象转化成json格式
      @RequestMapping("/test2")
      @ResponseBody
      public List test2() {
      User u = new User();
      u.setUsername("shan");
      u.setAge(18);
      return Arrays.asList(u, u, u);
      }

      //返回一个String,默认返回字符串是逻辑视图名称,加上@ResponseBody,当做json格式的数据
      @RequestMapping(value="/test3", produces="application/json;charset=utf-8")
      @ResponseBody
      public String test3() {
      return "success, 你好~";
      }

(2) @RestController = @Controller + @ResponseBody

(3) @RequestBody

  • @RequestBody: 处理请求,用于读取Http请求的内容,把json格式的请求数据封装成对象

    • application/x-www-form-urlencoded: 表单提交用得比较多,是传统的key-value格式,处理起来非常方便,无需RequestBody都可以,贴上也可以
    • application/multipart: 文件上传的请求,springmvc 装饰设计模式,既可以处理文件上传,也可以处理表单数据
    • application/json: 参数是json格式的,此时必须使用RequestBody\
    • application/xml

1、前台往后台传参转化为Date类型

★ 时间格式的注解:@DateTimeFormat

  • 注意细节:时间Date在util包(java中常用)有,在sql包也有,使用快捷键导包的时候可能会默认自动报错包~

    //从前台---->后台传递参数 java.lang.String -> java.util.Date
    //请求参数是Date类型
    @RequestMapping("/test1")
    public ModelAndView test(@DateTimeFormat(pattern = "yyyy-MM-dd")Date date) {
        System.out.println("date:" + date);
        if(date instanceof Date) {
            System.out.println("yes");
        }
        return null;
    }
  • 对象的属性有时间Date类型的,方式一:贴注解 @DateTimeFormat

    ◆ 方式二:数据绑定的时候处理,通过@InitBinder定义处理时间格式的方法

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        //日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //属性编辑器
        binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
    }
  • 让项目中的所有用到时间格式的类都使用到咱定义的处理时间格式的方法,咱将方法抽离出去,定义成一个类,贴上注解处理增强 @ControllerAdvice

    只要抽离的类在ioc的注解驱动扫描范围内,即可~

    @ControllerAdvice
    public class DateFormateAdvice {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        //日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //属性编辑器
        binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
    }

    }

2、jsp中显示时间格式:将欧美时间格式-->中国时间格式

(1)导入jar包[tomcat服务器examples案例下的lib就有]:

  • taglibs-standard-impl-1.2.5.jar
  • taglibs-standard-spec-1.2.5.jar

(2)配置引入标签库taglib

<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

(3) 使用jstl:

    <% pageContext.setAttribute("myDate", new java.util.Date()); %>
    北京时间: <fmt:formatDate value="${myDate}" pattern="yyyy=MM-dd HH:mm:ss"/>

3、后台往前台响应JSON时---Date类型

(1)方法1:在springmvc框架配置中添加json类型转化相关的配置[配置全局解析器]:

    <!-- MVC注解解析器 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                        <property name="dateFormat">
                            <bean class="java.text.SimpleDateFormat">
                                <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                            </bean>
                        </property>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

(2)方式2:使用注解@JsonFormat

@Data
public class User {
    private Long id;
    private String username;
    private Integer age;
    //@DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date hireDate;
}

★ 注意:@DateTimeFormat前台往后台传参【前台String类型-》后台Date类型】

★ 注意: 是后台响应给前台,响应为json格式(对时间Date类型的处理)

如果本文对你有帮助的话记得给一乐点个赞哦,感谢!