1. @RequestBody 问题
@RequestBody 只能处理自定义对象、map,、List,只能标记方法的一个参数对象,如果方法内有多个对象用@RequestBody 标记,需要重写Request头,来多次读取。@RequestBody 无法处理简单类型参数
2. 自定义注解@RequestSingleBody解决单个参数
自定义注解@RequestSingleBody
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.ValueConstants;
import java.lang.annotation.*;
/**
* @RequestSingleBody 注解,接收单个参数
*
* @author liuxb
* @date 2021/11/18 8:51
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestSingleBody {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
RequestSingleBodyHandler解析注解@RequestSingleBody
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cmbchina.base.Separators;
import com.cmbchina.base.annotation.RequestSingleBody;
import org.apache.commons.beanutils.ConvertUtils;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.Objects;
/**
* 解析注解 @RequestSingleBody,解决@RequestBody无法处理单json参数
*
* @author liuxb
* @date 2021/11/18 9:03
*/
public class RequestSingleBodyHandler implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestSingleBody.class) && parameter.getParameterAnnotation(RequestSingleBody.class).required();
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
RequestSingleBody requestSingleParam = parameter.getParameterAnnotation(RequestSingleBody.class);
//获取方法的参数名
String name = (requestSingleParam != null && org.springframework.util.StringUtils.hasLength(requestSingleParam.name()) ?
requestSingleParam.name() : parameter.getParameterName());
Assert.state(name != null, "Unresolvable parameter name");
//获取方法的参数类型
Class<?> parameterType = parameter.getParameterType();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int rd;
while ((rd = reader.read(buf)) != -1) {
sb.append(buf, 0, rd);
}
//只处理对象,数组形式不处理
if (sb.toString().startsWith(Separators.CURLY_BRAKET_OPEN)) {
//单字段只支持基本类型,包装类,字符串类型
Assert.isTrue(ClassUtils.isPrimitiveOrWrapper(parameterType) || parameterType.equals(String.class) ,
"@RequestSingleBody只支持简单类型,不支持复杂类型:" + parameterType.getName());
JSONObject jsonObject = JSONObject.parseObject(sb.toString());
return jsonObject.getObject(name, parameterType);
}
return null;
}
}
Separators 类
/**
* 分隔符类
*
* @author liuxb
* @date 2021/11/13 10:15
*/
public interface Separators {
/**
* 下划线
*/
String UNDERLINE = "_";
/**
* 中间杠
*/
String HYPHEN = "-";
/**
* 英文逗号
*/
String COMMA = ",";
/**
* 左斜杠
*/
String LSLASH = "/";
/**
* windwows文件路径分隔符
*/
String RSLASH = "\\";
/**
* 单冒号
*/
String COLON = ":";
/**
* 双冒号
*/
String DOUBLE_COLON = "::";
/**
* 单竖线
*/
String LINE = "|";
/**
* 双竖线
*/
String DOUBLE_LINE = "||";
/**
* 英文分号
*/
String SEMICOLON = ";";
/**
* 点
*/
String DOT = ".";
/**
* and符号
*/
String AND = "&";
/**
* @符号
*/
String AT = "@";
/**
* 星号
*/
String StAR = "*";
/**
* #符号
*/
String Comment_Sign = "#";
/**
* $符号
*/
String Dollar_Sign = "$";
/**
* 问号
*/
String QUESTION_MARK = "?";
/**
* 等于号
*/
String EQUAL = "=";
/**
* 左小括号
*/
String PARENTHESIS_OPEN = "(";
/**
* 右小括号
*/
String PARENTHESIS_CLOSE = ")";
/**
* 左大括号
*/
String CURLY_BRAKET_OPEN = "{";
/**
* 右大括号
*/
String CURLY_BRAKET_CLOSE = "}";
/**
* 左括号
*/
String BRAKET_OPEN = "[";
/**
* 右中括号
*/
String BRAKET_CLOSE = "]";
/**
* 空格
*/
String SPACE = " ";
/**
* 换行符
*/
String LINE_BREAK = "\n";
/**
* 换行符
*/
String R_BREAK = "\r";
}
注册注册自定义的参数解析器,过滤器可保证流多次读取
import com.cmbchina.base.handle.RequestSingleBodyHandler;
import com.cmbchina.base.converter.BodyReaderRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* webmvc配置
*
* @author liuxb
* @date 2021/11/13 17:19
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 注册自定义的参数解析器
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestSingleBodyHandler());
WebMvcConfigurer.super.addArgumentResolvers(argumentResolvers);
}
@Bean
public FilterRegistrationBean filterRegist() {
FilterRegistrationBean frBean = new FilterRegistrationBean();
frBean.setFilter(new BodyReaderRequestFilter());
//多个过滤器时指定过滤器的执行顺序
frBean.setOrder(1);
frBean.addUrlPatterns("/*");
return frBean;
}
}
controller的方法中使用
/**
* 根据ID查询单条记录
*
* @param id id
* @return
*/
@PostMapping("/findById")
public RetResult<RewardInfo> findById(@RequestSingleBody @NotNull(message="id不能为空") Integer id) {
return RetModel.ok().setData(rewardInfoService.findById(id));
}
3. @RequestBody标记多个参数
重写Request头
BodyReaderRequestFilter 过滤器
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* BodyReaderRequestFilter 过滤器 处理httpRequest包装类,实现request多次读取
*
* @author liuxb
* @date 2021/12/9 10:00
*/
public class BodyReaderRequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
BodyReaderRequestWrapper requestWrapper = new BodyReaderRequestWrapper(request);
if (requestWrapper == null) {
filterChain.doFilter(request, response);
} else {
filterChain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}
BodyReaderRequestWrapper 包装类
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* HttpServletRequest 包装类,实现多次从request中读取流
*
* @author liuxb
* @date 2021/12/4 18:37
*/
public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {
private final String body;
/**
* 构造器
* @param request
* @throws IOException
*/
public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder sb = new StringBuilder();
InputStream ins = request.getInputStream();
BufferedReader isr = null;
try {
if (ins != null) {
isr = new BufferedReader(new InputStreamReader(ins));
char[] charBuffer = new char[128];
int readCount = 0;
while ((readCount = isr.read(charBuffer)) != -1) {
sb.append(charBuffer, 0, readCount);
}
} else {
sb.append("");
}
} catch (IOException e) {
throw e;
} finally {
if (isr != null) {
isr.close();
}
}
sb.toString();
body = sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletIns = 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 byteArrayIns.read();
}
};
return servletIns;
}
}
注册过滤器
@Bean
public FilterRegistrationBean filterRegist() {
FilterRegistrationBean frBean = new FilterRegistrationBean();
frBean.setFilter(new BodyReaderRequestFilter());
//多个过滤器时指定过滤器的执行顺序
frBean.setOrder(1);
frBean.addUrlPatterns("/*");
return frBean;
}
4. 测试
@Validated
@RestController
@RequestMapping("/activityInfo")
public class ActivityInfoController {
@Autowired
private ActivityInfoService activityInfoService;
/**
* 分页查询
*
* @param activityInfo
* @return
*/
@PostMapping("/findByPage")
public RetResult<PageResult<ActivityInfo>> findByPage(@RequestBody ActivityInfo activityInfo, @RequestBody Page page) {
return RetModel.ok().setData(activityInfoService.findByPage(activityInfo, page));
}
/**
* 根据id查询单条记录
*
* @param id id
* @return
*/
@PostMapping("/findById")
public RetResult<ActivityInfo> findById(@RequestSingleBody @NotBlank(message = "id不能为空") String id) {
return RetModel.ok().setData(activityInfoService.findById(id));
}
}