SpringMVC-04-结果跳转及数据处理

1、结果跳转

SpringMVC中有两种实现 Handler 的方式:接口实现 和 注解实现,
两种方式对请求结果的处理各有不同。

1.1、接口Handler处理结果

public class ControllerTest implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = new ModelAndView();mv.addObject("msg", "ControllerTest");mv.setViewName("/test");return mv;}
}

接口Handler使用ModelAndView对象处理结果

ModelAndView是SpringMVC中的一种 中间数据对象,
封装了 Model层处理后的结果数据 和 将要跳转视图的逻辑视图名,
走视图解析器,逻辑视图名拼接前后缀。

1.2、注解Handler处理结果

@Controller
@RequestMapping("/h1")
public class HandlerMethodTest {// 返回值 void 参数有 resp , 结果:不走视图解析器,响应自然返回,由 resp 控制@RequestMapping("/t1")public void test1(HttpServletResponse resp) {System.out.println("方法参数为:resp");}// 返回值 void 参数无 resp , 结果:走视图解析器,逻辑视图名 默认为 对应的RequestMappingInfo的路径@RequestMapping("/t2")public void test2(HttpServletRequest req, Model model) throws IOException {model.addAttribute("msg", "test2");System.out.println("方法参数为:req + model");}// 返回值 String 无论参数 ,// 结果1(没被 @ResponseBody 标注):统一走视图解析器,逻辑视图名 为返回的String值// 结果2(被 @ResponseBody 标注):不走视图解析器,返回的String值被当作响应体返回@RequestMapping("/t3")@ResponseBodypublic String test3(HttpServletRequest req, HttpServletResponse resp, Model model) throws Exception {model.addAttribute("msg", "test3");return "test";}}

注解Handler,即HandlerMethod,
其相应的Handler适配器 根据其返回值方法参数的不同,有不同的执行策略:

  • 返回值 void

    • 方法参数有 HttpServletResponse

        不走视图解析器,响应自然返回,由 resp 控制

    • 方法参数无 HttpServletResponse

        走视图解析器,逻辑视图名 默认为 对应的RequestMappingInfo的路径

  • 返回值 String,无论参数

    • 没被 @ResponseBody 标注

        统一走视图解析器,逻辑视图名 为返回的String值

    • 被 @ResponseBody 标注

        不走视图解析器,返回的String值被当作响应体直接返回给客户端浏览器

@ResponseBody 作用:

  改变 HandlerMethod 的返回值意义,把其当作响应体而不是逻辑视图名直接返回给客户端浏览器,一般标注在返回值为String的 HandlerMethod 上,因为返回的响应体为空值没有意义。

1.3、forward与redirect

@Controller
@RequestMapping("/r1")
public class ResultController {@RequestMapping("/t1")public String test(Model model) {model.addAttribute("msg", "Result1");return "test";}@RequestMapping("/t2")public String test2(Model model) {// 转发:forward 视图解析器的特殊前缀,对后面的路径执行转发操作,不做逻辑视图名那样的前后缀拼接model.addAttribute("msg", "Result2");return "forward:/WEB-INF/jsp/test.jsp";}@RequestMapping("/t3")public String test3(Model model) {// 重定向:redirect 视图解析器的特殊前缀,对后面的路径执行重定向操作,不做逻辑视图名那样的前后缀拼接model.addAttribute("msg", "Result3");return "redirect:/r1/t2";}}

forward:redirect:为 视图解析器的特殊前缀,
对后面的路径执行转发或者重定向操作,不做逻辑视图名那样的前后缀拼接。

image-20240712030838172

官方文档翻译:

指定转发或重定向URL的特殊视图名称的前缀(通常在表单提交和处理后发送给控制器)。
此类视图名称将不会以配置的默认方式解析,而是被视为特殊的快捷方式。


2、参数数据绑定

在SpringMVC 使用注解处理请求的方式中,框架会对 HandlerMethod 的方法参数 进行数据绑定,以便于更简洁的处理请求。

数据绑定的方式分为三种:传统数据绑定、路径变量绑定 和 特定参数绑定
三种绑定方式对于不同的方法参数皆有不同的处理。

注意:后端获取前端数据,都是把它当成String类型获取,再将其解析转换成相应的类型

2.1、传统数据绑定

获取 请求参数 为绑定值:request.getParameter("参数名")

  • 普通参数

    默认绑定的请求参数 与 方法参数 同名,非必要(即,可以不传相应参数,值为 null)
    若要绑定其他请求参数,可用 @RequestParam 注解 指定参数名称 和 是否必要

        @GetMapping("/t1")public String t1(@RequestParam("username") String name, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + name);// 2.将返回的值传递给前端model.addAttribute("msg", name);// 3.跳转视图return "test";}
    

    image-20240716010627532

    image-20240716010653954

  • 实体类

    框架会先创建这个实体类的对象,然后通过类的 属性名 去和 请求参数 进行数据绑定,属性绑定请求参数,非必要

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    class User {private Integer id;private String name;private String pwd;
    }
    
        @GetMapping("/t2")public String t2(User user, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + user.toString());// 2.将返回的值传递给前端model.addAttribute("msg", user.toString());// 3.跳转视图return "test";}
    

    image-20240716022035872

    image-20240716022055713

  • 万能的Map

    Map类型的参数 可以收集绑定 所有的请求参数,必须和 @RequestParam 注解一起使用,不然框架只会传一个空Map进来
    使用Map参数,会让程序更加灵活多变,具备扩展性

        @GetMapping("/t3")public String t3(@RequestParam Map<String, Object> map, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + map.toString());// 2.将返回的值传递给前端model.addAttribute("msg", map.toString());// 3.跳转视图return "test";}
    

    image-20240716023530523

    image-20240716023545608

2.2、路径变量绑定

路径变量,即 URI模板变量,是一种简化URL配置的方式,它允许你使用占位符来表示路径动态变化的部分。

编写方式:/{占位符1}/{占位符2}……

例子:/users/{userId}/posts/{postId},userIdpostId即是路径变量,变量值由前端具体的URL格式化得来。

路径变量 可以被绑定到 HandlerMethod 的方法参数上,这是一种将前端参数写入URL路径的方式,
其只用URL来表示具体的资源位置,符合Rest风格。

获取 路径变量 为绑定值:格式化具体的URL得来

  • 普通参数

    必须为方法参数标注 @PathVariable 注解
    默认绑定的路径变量 与 方法参数 同名,必要
    若要绑定其他的路径变量,可用 @PathVariable 注解 指定变量名称 和 是否必要

        @GetMapping("/t4/{username}/{age}")public String t4(@PathVariable("username") String name, @PathVariable Integer age, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + name);System.out.println("接收到前端的参数为:" + age);// 2.将返回的值传递给前端model.addAttribute("msg", name + "-" + age);// 3.跳转视图return "test";}
    

    image-20240716032143980

    image-20240716032159875

  • 实体类

    框架会先创建这个实体类的对象,然后通过类的 属性名 去和 路径变量 进行数据绑定,属性绑定路径变量,非必要

        @GetMapping("/t5/{id}/{name}/{pwd}")public String t5(User user, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + user.toString());// 2.将返回的值传递给前端model.addAttribute("msg", user.toString());// 3.跳转视图return "test";}
    

    image-20240716032601360

    image-20240716032622301

2.3、特定参数绑定

HandlerMethod 的方法参数中有一些特定的类型,比如:HttpServletRequest、HttpServletResponse、Model、ModelMap……
它们做数据绑定时,由框架传入特定的对象
这些类型基本上都是框架内部组织的一部分:
HttpServletRequest 代表 请求、
HttpServletResponse 代表 响应、
Model、ModelMap 代表 中间数据容器

获取 框架内部对象 为绑定值:由框架传入

  • 特定参数

        @GetMapping("/t6")public String t6(HttpServletRequest req, HttpServletResponse resp, Model model) {// 1.接收前端参数System.out.println("接收到前端的参数为:" + req);System.out.println("接收到前端的参数为:" + resp);// 2.将返回的值传递给前端model.addAttribute("msg", req);model.addAttribute("msg2", resp);// 3.跳转视图return "test";}
    

    image-20240716034947947

    image-20240716035008537

3、中间数据对象

  • ModelAndView

    模型层结果数据 和 视图信息 的结合封装,内部其实组合了ModelMap对象,一般用于 接口Handler 处

    public class ControllerTest1 implements Controller {public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {//返回一个模型视图对象ModelAndView mv = new ModelAndView();mv.addObject("msg","ControllerTest1");mv.setViewName("test");return mv;}
    }
    
  • ModelMap

    实现了Map接口的实现类,用于存储 模型层结果数据 ,常用于 注解Handler 处

    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name, ModelMap model){//封装要显示到视图中的数据//相当于req.setAttribute("name",name);model.addAttribute("name",name);System.out.println(name);return "hello";
    }
    
  • Model

    模型数据存储对象 的标准接口,代表了规范的定义,常用于 注解Handler 处
    值得一提的是,Model的实现类大都继承了ModelMap

    @RequestMapping("/hello2")
    public String hello(@RequestParam("username") String name, Model model){//封装要显示到视图中的数据//相当于req.setAttribute("name",name);model.addAttribute("msg",name);System.out.println(name);return "test";
    }
    

三者对比

就对于新手而言简单来说,使用区别就是:

Model 只是标准接口,只有寥寥几个方法用于储存数据,简化了新手对于Model对象的操作和理解;ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图名,控制视图层的跳转。

当然,以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。

三者关系

关系图

4、乱码问题

测试步骤:

  1. 编写一个提交的表单 /WEB-INF/jsp/EncodeTest.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>EncodeTest</title>
    </head>
    <body><p>Get表单: </p>
    <form action="/spring04/e/t" method="get"><input type="text" name="name"><input type="submit">
    </form><p>Post表单: </p>
    <form action="/spring04/e/t" method="post"><input type="text" name="name"><input type="submit">
    </form>${msg}</body>
    </html>
    
  2. 后台编写对应的处理类

    @Controller
    public class Encoding {@RequestMapping("/e/t")public String test(String name, Model model) throws UnsupportedEncodingException {System.out.println(name);model.addAttribute("msg", name); //获取表单提交的前端参数return "EncodeTest"; //跳转到视图显示输入的值}}
    
  3. Get 中文参数测试,结果:显示正常,无乱码

    image-20240717194722573

    image-20240717194737567

  4. Post 中文参数测试,结果:出现乱码

    image-20240717194921147

    image-20240717194933016

Post请求中文参数乱码

原因分析:

总所周知,Post参数数据存储在请求体里面,以页面编码解码,变成字节流形式存储,以二进制流的形式发送到的服务器。
服务器收到数据后,以默认编码进行编码。

这里,我的服务器用的是Tomcat9,默认编码为 ISO-8859-1 ,页面编码用的是 UTF-8
前后端编码不一致,导致乱码。

image-20240717205406753

解决:

给服务器设置解析请求的字符集即可,request.setCharacterEncoding("UTF-8");

在项目中,可以写一个过滤器来操作,这里我们可以使用SpringMVC提供的字符集过滤器,在web.xml中配置

    <filter><filter-name>Encoding</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>Encoding</filter-name><servlet-name>DispatcherServlet</servlet-name></filter-mapping>

当然,我们也可以自定义过滤器,这里提供网上一位大神的写的

package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/*** 解决get和post请求 全部乱码的过滤器*/
public class GenericEncodingFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {//处理response的字符编码HttpServletResponse myResponse=(HttpServletResponse) response;myResponse.setContentType("text/html;charset=UTF-8");// 转型为与协议相关对象HttpServletRequest httpServletRequest = (HttpServletRequest) request;// 对request包装增强HttpServletRequest myrequest = new MyRequest(httpServletRequest);chain.doFilter(myrequest, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {private HttpServletRequest request;//是否编码的标记private boolean hasEncode;//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰public MyRequest(HttpServletRequest request) {super(request);// super必须写this.request = request;}// 对需要增强方法 进行覆盖@Overridepublic Map getParameterMap() {// 先获得请求方式String method = request.getMethod();if (method.equalsIgnoreCase("post")) {// post请求try {// 处理post乱码request.setCharacterEncoding("utf-8");return request.getParameterMap();} catch (UnsupportedEncodingException e) {e.printStackTrace();}} else if (method.equalsIgnoreCase("get")) {// get请求Map<String, String[]> parameterMap = request.getParameterMap();if (!hasEncode) { // 确保get手动编码逻辑只运行一次for (String parameterName : parameterMap.keySet()) {String[] values = parameterMap.get(parameterName);if (values != null) {for (int i = 0; i < values.length; i++) {try {// 处理get乱码values[i] = new String(values[i].getBytes("ISO-8859-1"), "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}}hasEncode = true;}return parameterMap;}return super.getParameterMap();}//取一个值@Overridepublic String getParameter(String name) {Map<String, String[]> parameterMap = getParameterMap();String[] values = parameterMap.get(name);if (values == null) {return null;}return values[0]; // 取回参数的第一个值}//取所有值@Overridepublic String[] getParameterValues(String name) {Map<String, String[]> parameterMap = getParameterMap();String[] values = parameterMap.get(name);return values;}
}

Get请求无乱码分析

在上面的测试中可以看到,在Post请求乱码的情况下,Get请求能够正常显示中文字符,这是什么原因呢?

Get请求的参数写在URL中,一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。
这是因为网络标准RFC 1738做了硬性规定:

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL.""只有字母和数字[0-9a-zA-Z]、一些特殊符号"$-_.+!*'(),"[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。"

这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致"URL编码"成为了一个混乱的领域。
想深入的可以去看看 关于URL编码 这篇文章,初学者只需要知道现在大部分情况下,浏览器都使用 UTF-8 作为URL编码。

于是,就上面的测试而言,
http://localhost:8080/spring04/e/t?name=斗破苍穹
经过 UTF-8 编码转换,得到
http://localhost:8080/spring04/e/t?name=斗破苍穹
然后,服务器接收到请求,用指定解析URL的编码对其进行解码,得到参数值

Tomcat9 解析URL的默认编码为 UTF-8,具体的值可以在Tomcat的 server.xml 中进行配置
即,Connector标签中的 URIEncoding 属性

<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />

乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/745536.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SciTech-EECS-电路设计- PCB设计-PCB设计流程 + 元器件封装设计 + PCB设计规则 +PCB布局/布线/覆铜等设计

PCB(印刷电路板)设计, 是以"电路原理图"为根据实现电路设计者所需要的功能。 "PCB设计"主要指"版图设计", 需要考虑:"外部Connections(连接)"的布局, "内部Components(电子元器件)的优化布局, "金属连线" 和 "Via…

全网最适合入门的面向对象编程教程:19 类和对象的 Python 实现-使用 PyCharm 自动生成文件注释和函数注释

本文主要介绍了在使用Python的IDE-PyCharm时,如何在新建文件时自动添加文件注释的方法,同时对PyCharm中生成函数注释的方法进行了简单介绍。全网最适合入门的面向对象编程教程:19 类和对象的 Python 实现-使用 PyCharm 自动生成文件注释和函数注释 摘要: 本文主要介绍了在使…

spring email 发送功能

邮件通知是现代应用中常见的一种通信方式,特别是在需要及时反馈、告警或重要事件通知的场景下。Spring Boot提供了简单而强大的邮件发送功能,本文将通过pring Boot中使用JavaMailSender 接口实现邮件发送。使用场景说明注册验证:在用户注册、商户注册时,通过邮件发送验证码…

T240718(辐角函数连续性)

复变函数辐角函数的连续性[T240718] 证明复变函数 \(\arg z ~(-\pi<\arg z\le \pi)\) 在负实轴上 (包括原点) 不连续, 除此之外在 \(z\) 平面上处处连续. 证:当 \(z=0\) 时, \(\arg z\) 无意义, 自然不连续. 在负实轴上任取一点 \(z_0\), 当 \(z\) 从上半平面趋于 \(z_0\) …

缺陷和缺陷报告

一、缺陷的基本概述1、缺陷的定义 软件未实现产品说明书要求的功能软件出现了产品说明书指明不应该出现的功能软件实现了产品说明书未提到的功能软件未实现产品说明书虽未明确提及在应该实现的目标软件难以理解、不易使用、运行缓慢或者(从测试的角度看)最终用户会认为不好 2…

用Python实现批量扫描域名是否存在指定文件

初学Python练手项目,直接上代码,后续技术进步了可以加上指定字典扫描,现在还不会^_^ 加上并发的话扫描速度会更快,现在也不会~1 import requests2 3 with open(domains.txt,r) as file:4 domains =[line.strip() for line in file]5 for domain in domains:6 try:7…

海量数据场景面试题:出现频率最高的 100 个词

用两种解法解面试题:出现频率最高的 100 个词题目描述 假如有一个 1G 大小的文件,文件里每一行是一个词,每个词的大小不超过 16 bytes,要求返回出现频率最高的 100 个词。内存限制是 10M。 解法1 由于内存限制,所以我们没有办法一次性把大文件里面的所有内容一次性读取到内…

Loki的API接口

参考 Loki HTTP 接口 |Grafana Loki 文档 状态接口 这些 HTTP 端点由所有组件公开,并返回组件的状态:GET /ready GET /log_level GET /metrics GET /config GET /services GET /loki/api/v1/status/buildinfo$ curl 127.0.0.1:3100/ready ready$ curl 127.0.0.1:3100/log_lev…

单体模式部署Loki服务

将Loki的所有微服务组件打包部署到单一进程中 适合小规模系统的日志存储场景(每天不超过100G) 在必要时,可部署共享外部对象存储的多实例进行水平扩容在配置文件loki.yaml的ring配置段中定义日志数据的跨实例分发支持高可的部署方式多个实例需要配置共享的外部对象存储 需要…

DTO转VO工具

data工具,实现了对象拷贝 DTO -> VOdata工具,实现了对象拷贝 DTO -> VO 只需要实现一个类即可 data-utils data工具,实现了对象拷贝DTO —> VO 解决的问题 Mapstruct需要安插件!!!!很多云桌面等会很不方便org.springframework.beans.BeanUtils有一个 copyPropertie…

【Python】公众号聚合登录软件+源码

废话不多说了,直接上图,回复拿软件和源码【自己打包,配置环境比较复杂】 写这个软件就是因为其他平台的会员太贵了,还不如自己写个,不限制账号登录数~授权,打开和删除功能都是正常的, 面板功能,我打算做一个单独的页面【但是不知道做啥,就先放这里,希望各位老板提供想…

HuggingFace Transformers

HuggingFace Transformers https://www.hugging-face.org/hugging-face-transformers-2/Hugging Face Transformers has been built by, with, and for the community. Reaching 100k on GitHub is a testament to ML’s reach and the community’s will to innovate and cont…