前言: 在使用自定义注解获取接口中形参中的参数或者实体类对象数据的时候,需要用到拦截器去拦截每次请求,但在拦截器使用getInputStream()或者getReader()方法后会导致后面的执行无法获取到数据(也就是使用@RequestBody注解),这是因为这两个方法只能读一次数据流之后会失效,本人亲测在HandlerMethod对象中能获取到方法入参参数,但无法获取到实体类数据,整了有点时间查询相关文章解决后分享
新建Demo项目 引入Maven依赖<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 打包跳过单元测试 -->
<skipTests>true</skipTests>
</properties>
<dependencies>
<!-- 添加springboot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 添加工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
引入SpringBoot启动类
/**
* @author Edward
* @version V1.0
* @className RequestStreamApplication
* @description:
* @date 2022/4/26 16:10
**/
@SpringBootApplication
public class RequestStreamApplication {
public static void main(String[] args) {
SpringApplication.run(RequestStreamApplication.class);
}
}
创建实体类和Controller类
public class Student {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
/**
* @author edward
* @className requestStreamController
* @description:
* @date 2022/4/26 16:12
**/
@RestController
public class RequestStreamController {
private static final Logger log = LoggerFactory.getLogger(RequestStreamController.class);
@PostMapping(value = "/requestDemo")
@Permission
public String requestDemo(@RequestBody Student student){
log.info(student.toString());
return student.toString();
}
}
创建自定义注解类
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
boolean isOpen() default true;
}
配置文件
server:
port: 8085
servlet:
context-path: /requestStream
创建自定义RequestWrapper装饰类
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @author edward
* @className RequestWrapper
* @description: 自定义Request装饰类,用于处理Request请求中post方式Body内容不能重复读问题
* @date 2022/4/26 16:33
**/
public class RequestWrapper extends HttpServletRequestWrapper {
/* 声明字节数组 */
private byte[] requestBody;
/* 声明request对象 */
private HttpServletRequest request;
/* 构造器将形参传递到类对象HttpServletRequest中 */
public RequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
/**
* @return ServletInputStream
* @throws IOException
* @description: 重写Request中getInputStream方法
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if(null == requestBody){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(),baos);
/* 转字节数组 */
this.requestBody = baos.toByteArray();
}
/* 创建ServletInputStream,将复制的字节流存入特定方法中 */
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
/**
* @return BufferedReader
* @throws IOException
* @description: 创建输入流Reader对象,调用已重写的输入流对象
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
创建自定义过滤器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author edward
* @className ChannelFilter
* @description: Channel过滤器,用于载入对自定义Request装饰类并放行
* @date 2022/4/26 16:23
**/
@Component
@WebFilter(filterName = "channelFilter",urlPatterns = {"/*"})
public class ChannelFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
/* 不重写 */
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
ServletRequest requestWrapper = null;
/* 判断当前请求是否属于Http类型,是的话创建自定义Request装饰类 */
if(request instanceof HttpServletRequest){
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
/* 自定义Request装饰类对象不为空时放行传给下一个过滤器或者直达Servlet,为空时传入默认Request对象 */
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
/* 不重写 */
}
}
创建自定义拦截器
说明:这里是利用@Permission注解判断,如果学生名为alice则被拦截无法进入接口,否则就放行
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.edward.annotation.Permission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
/**
* @author edward
* @className MyInterceptor
* @description: 自定义处理器拦截器
* @date 2022/4/26 16:59
**/
@Component
public class MyInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
/* 强转HandlerMethod对象 */
HandlerMethod handlerMethod = (HandlerMethod) handler;
/* 每次请求判断该注解是否在方法上 */
boolean isAnnotation = handlerMethod.hasMethodAnnotation(Permission.class);
if(isAnnotation){
Permission perm = handlerMethod.getMethodAnnotation(Permission.class);
boolean open = perm.isOpen();
if(open){
BufferedReader reader = request.getReader();
String str = "";
StringBuilder stringBuilder = new StringBuilder();
/* 循环取出Post方式下请求对象的body数据 */
while((str = reader.readLine())!= null){
stringBuilder.append(str);
}
/* 创建Jackson对象 */
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.readValue(stringBuilder.toString(), Map.class);
if("alice".equals(map.get("name"))){
response.setContentType("application/json;charset=utf-8");
HashMap<String,Object> returnMap = new HashMap<>();
returnMap.put("message","学生名为alice被拦截");
String value = objectMapper.writeValueAsString(returnMap);
/* 返回响应信息 */
response.getWriter().print(value);
/* false为拦截,true为放行 */
return false;
}
}
}
}
return true;
}
}
创建WebMvc配置类,用于添加自定义拦截器
说明:这里实现WebMvcConfigurer或继承WebMvcConfigurationSupport,两种只能选择其中一种,同时配置会导致其中一个失效
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author edward
* @className WebAppConfigurer
* @description: WebMvc配置类,用于添加自定义拦截器
* @date 2022/4/26 17:40
**/
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
/* 配置自定义拦截器 */
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
测试结果
这里字段名为name的alice被拦截了
字段名为name的edward不会拦截
参考文章:https://blog.csdn.net/qq_33548675/article/details/121015317
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)