【小家Spring】Spring MVC好用工具介绍:UrlPathHelper、WebUtils、RequestContextUtils、WebApplicationContextUtils...(上)

简介: 【小家Spring】Spring MVC好用工具介绍:UrlPathHelper、WebUtils、RequestContextUtils、WebApplicationContextUtils...(上)

前言


随着struts2漏洞的出现,以及struts2使用的不方便,过重的设计。所以市面上MVC的实际标已经成了Spring MVC。


因此本文主要针对Spring MVC的web环境下,Spring-web提供的这个jar里的util包内的一些类,因为都是比较共用的一些web类,因此在这里做一些介绍~

实用类介绍(排名不分先后)



image.png


ContentCachingRequestWrapper、ContentCachingResponseWrapper


字面理解:把请求/响应内容缓存起来的代理类~


这两个类出现的版本比较晚(如下),之前我们一直实用的HttpServletRequestWrapper,现在有了它,使用起来就更加的方便了~

/**
* @since 4.1.3
*/
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {}


我们都知道request.getInputStream()请求body里面的内容只能被获取一次的。通过这个类(但其实有bug):

**它能够解决:解决HttpServletRequest inputStream只能读取一次的问题**

**它能够解决:解决HttpServletRequest inputStream只能读取一次的问题**

上篇博文 【小家Spring】从OncePerRequestFilter的源码解读去看看Spring里面的Filter有什么特别以及常用Filter使用介绍

最后讲到请求日志里看到,payload只有请求结束的时候才打印出来,为何呢?


我们看看源码:

private final ByteArrayOutputStream cachedContent;


它使用一个字段:cachedContent来缓存body体里面的内容。但最重要的是,什么时候才会向cachedCotent里面写内容呢?我们继续跟踪发现:大体有两个地方:


public String getParameter(String name);
public Map<String, String[]> getParameterMap();
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name)
//都有这个判断。必须没被写过,并且是表单形式的Post方式才会往里写内容(这是一种非常特殊的传值方式,使用较少)
if (this.cachedContent.size() == 0 && isFormPost()) {
  writeRequestParametersToCachedContent();
}


第二种方式才是重点:


  @Override
  public int read() throws IOException {
    int ch = this.is.read();
    if (ch != -1 && !this.overflow) {
      if (contentCacheLimit != null && cachedContent.size() == contentCacheLimit) {
        this.overflow = true;
        handleContentOverflow(contentCacheLimit);
      }
      else {
        cachedContent.write(ch);
      }
    }
    return ch;
  }


我们发现还是在read()方法里面,只有调用了请求流的read方法,才会把内容缓存起来。这个和Spring MVC的原理:@RequestBody注解的参数解析器(RequestResponseBodyMethodProcessor)就是调用了read()方法去获取到内容的。

到此我们其实能够很好的解释,上篇博文里为何请求开始没有payload,请求结束时有了


但是这么做,很多时候并不能满足我们的使用场景:

1、我们需要在filter中拿到requestBody数据进行处理(比如检查敏感词汇,过滤掉低俗的词汇等~)

2、在controller中注入@ReqeustBody读取rerquestBody数据


按照Spring目前的设计,这个request只要我们getInputStream()就不能再被controller接受了(·requestBody is missing…·),显然不是我们想要的。这个其实是Spring的一个bug,早在2014年就有人提出了,只是一直都没有被修复:this is a bug


在读取的时候应该先去缓存看看,有值就不要读流了,返回即可。这样缓存里面的数据是可以玩限次重复读的。在还没有这个类的时候,我写了一个wrapper,供以参考:


public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{
    private byte[] body;
    private BufferedReader reader;
    private ServletInputStream inputStream;
    public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
        super(request);
        //读一次 然后缓存起来
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new RequestCachingInputStream(body);
    }
    public byte[] getBody() {
        return body;
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {          
            return inputStream;
        }
        return super.getInputStream();
    }
    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }
    //代理一下ServletInputStream 里面真是内容为当前缓存的bytes
    private static class RequestCachingInputStream extends ServletInputStream {
        private final ByteArrayInputStream inputStream;
        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }
        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }
        @Override
        public boolean isReady() {
            return true;
        }
        @Override
        public void setReadListener(ReadListener readlistener) {
        }
    }
}


因此,千万千万不要在Filter里提前去getInputStream(),否则@RequestBody将拿不到东西而报错的。使用此类的时候需要注意~


CookieGenerator


顾名思义,是生成Cookie的。使用起来也比较简单,不做介绍。一般在CAS单点登录里用得较多。现在都JWT了,就用得较少了


HtmlUtils


很多时候,由于特殊字符的原因,会造成用户输入的信息反馈到页面上时会显示成乱码,造成页面排版混乱;另外,黑客经常利用特殊字符对网站进行xss跨站攻击,所以我们需要对页面上提交的特殊字符进行html转码。


Spring提供的这个工具类,省去了我们写工具类对html中的特殊字符进行过滤的麻烦。

    public static void main(String[] args) {
        String specialStr = "#<table id=\"testid\"><tr>test1;test2</tr></table>";
        // 转义(用转义字符表示): #&lt;table id=&quot;testid&quot;&gt;&lt;tr&gt;test1;test2&lt;/tr&gt;&lt;/table&gt;
        String str1 = HtmlUtils.htmlEscape(specialStr);
        System.out.println(str1);
        // 转义(用数字字符表示): #&#60;table id=&#34;testid&#34;&#62;&#60;tr&#62;test1;test2&#60;/tr&#62;&#60;/table&#62;
        String str2 = HtmlUtils.htmlEscapeDecimal(specialStr);
        System.out.println(str2);
        // 转义(用16进制表示)
        String str3 = HtmlUtils.htmlEscapeHex(specialStr);
        System.out.println(str3);
        // 返转义,一个方法即可 结果见上面 specialStr
        System.out.println(HtmlUtils.htmlUnescape(str1));
        System.out.println(HtmlUtils.htmlUnescape(str2));
        System.out.println(HtmlUtils.htmlUnescape(str3));
    }


这样转义后是安全的。比如用编辑器编辑成良好格式的HTML串传过来。我们一般要过滤掉<script>这种标签,防止被黑

另外JavaScriptUtils可以js里面的一些特殊符号转义。如’ // 等等符号

相关文章
|
6月前
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
315 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
477 0
|
6月前
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
290 0
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
257 0
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
201 0
|
2月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
177 0
|
2月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
110 0
|
2月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
148 0
|
8月前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
359 29
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
725 0

热门文章

最新文章