【小家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里面的一些特殊符号转义。如’ // 等等符号

相关文章
|
19天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
8天前
|
Java 应用服务中间件 Spring
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
|
7天前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
2月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
2月前
|
XML JSON 数据库
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
这篇文章详细介绍了RESTful的概念、实现方式,以及如何在SpringMVC中使用HiddenHttpMethodFilter来处理PUT和DELETE请求,并通过具体代码案例分析了RESTful的使用。
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
|
2月前
|
前端开发 应用服务中间件 数据库
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
这篇文章通过一个具体的项目案例,详细讲解了如何使用SpringMVC、Thymeleaf、Bootstrap以及RESTful风格接口来实现员工信息的增删改查功能。文章提供了项目结构、配置文件、控制器、数据访问对象、实体类和前端页面的完整源码,并展示了实现效果的截图。项目的目的是锻炼使用RESTful风格的接口开发,虽然数据是假数据并未连接数据库,但提供了一个很好的实践机会。文章最后强调了这一章节主要是为了练习RESTful,其他方面暂不考虑。
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
|
2月前
|
JSON 前端开发 Java
Spring MVC返回JSON数据
综上所述,Spring MVC提供了灵活、强大的方式来支持返回JSON数据,从直接使用 `@ResponseBody`及 `@RestController`注解,到通过配置消息转换器和异常处理器,开发人员可以根据具体需求选择合适的实现方式。
94 4
|
2月前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
88 3
|
2月前
|
安全 Java Spring
Spring Boot 关闭 Actuator ,满足安全工具扫描
Spring Boot 关闭 Actuator ,满足安全工具扫描
85 0
|
2月前
|
前端开发 Java Spring
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
48 0
下一篇
无影云桌面