1.:Required request body is missing 及HttpServletRequestWrapper的使用
SpringBoot的Controller中,由于新添加的Filter中对请求流的处理,导致json解析失败,Required request body is missing的错误。
错误场景
1.新增的filter
package com.nt.demo.config.filters;
import com.nt.demo.config.wrapper.WrapperServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Create by TaoTaoNing
* 2019/7/3
**/
@Slf4j
@Order(0)
@WebFilter(urlPatterns = "/*", filterName = "loginFilter")
public class LoginFilter implements Filter {
private static final String SESSION_TOKEN = "user_name";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info(this.getClass().getSimpleName() + " -- " + "is init ------");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info(servletRequest.getContentType());
HttpServletResponse servletResponse1 = (HttpServletResponse) servletResponse;
servletResponse1.setHeader("ningtao", "test_header");
// 从这里开始是导致错误的原因
BufferedReader br = new BufferedReader(new InputStreamReader(servletResponse1 .getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
// readLine自带flush效果
while ((line = br.readLine()) != null) {
System.out.println(line);
sb.append(line);
}
System.out.println(sb.toString());
filterChain.doFilter(servletResponse1 , servletResponse);
}
@Override
public void destroy() {
log.info(this.getClass().getSimpleName() + " -- " + "is destroy ------");
}
}
2.Controller
package com.nt.demo.controller;
import com.nt.demo.entity.UserDTO;
import com.nt.demo.intf.TestService;
import com.nt.demo.pojo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* Create by TaoTaoNing
* 2019/7/4
**/
@Slf4j
@RestController
@RequestMapping("/core")
public class CoreController {
@Reference
private TestService testService;
@PostMapping("/login")
public String checkLogin(@RequestBody @Valid UserVO userVO, BindingResult errors){
log.info(userVO.toString());
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(userVO,userDTO);
UserDTO userDTO1 = testService.selectUser(userDTO);
if (null != userDTO1){
return "success";
}
return "fail";
}
}
3.运行结果:
{
"timestamp": "20190724151800",
"status": 400,
"error": "Bad Request",
"message": "Required request body is missing: public java.lang.String com.nt.demo.controller.CoreController.checkLogin(com.nt.demo.pojo.UserVO,org.springframework.validation.BindingResult)",
"trace": "org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.nt.demo.controller.CoreController.checkLogin(com.nt.demo.pojo.UserVO,org.springframework.validation.BindingResult)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:160)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:126)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat com.nt.demo.config.filters.LoginFilter.doFilter(LoginFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n",
"path": "/demo/core/login"
}
改正
- 新增请求包装类
package com.nt.demo.config.wrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* Create by TaoTaoNing
* 2019/7/24
* 这个类的作用相当于将请求流中的信息保存起来,后续的对请求流的操作都使用该对象获取流
* 可以达到类似请求流复制的效果,可以有效避免controller中参数封装之前,请求流中信息就已经被读取出来,
* 抛出:Required request body is missing 异常
**/
@Slf4j
public class WrapperServletRequest extends HttpServletRequestWrapper {
private byte[] buffer;
public WrapperServletRequest(HttpServletRequest request) {
super(request);
try {
// 读取输入流里面的参数,保存到buff 缓存中
buffer = IOUtils.toByteArray(request.getInputStream());
} catch (IOException e) {
log.error(e.getMessage());
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new BufferedServletInputStream(this.buffer);
}
class BufferedServletInputStream extends ServletInputStream {
private ByteArrayInputStream byteArrayInputStream;
private boolean isFinished = false;
public BufferedServletInputStream(byte[] buffer) {
this.byteArrayInputStream = new ByteArrayInputStream(buffer);
}
@Override
public int available() throws IOException {
return byteArrayInputStream.available();
}
@Override
public int read() {
int read = byteArrayInputStream.read();
if (-1 == read) {
isFinished = true;
}
return read;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read = byteArrayInputStream.read(b, off, len);
if (-1 == read) {
isFinished = true;
}
return read;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
@Override
public boolean isReady() {
return true;
}
@Override
public boolean isFinished() {
return isFinished;
}
}
}
- filter修正
package com.nt.demo.config.filters;
import com.nt.demo.config.wrapper.WrapperServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Create by TaoTaoNing
* 2019/7/3
**/
@Slf4j
@Order(0)
@WebFilter(urlPatterns = "/*", filterName = "loginFilter")
public class LoginFilter implements Filter {
private static final String SESSION_TOKEN = "user_name";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info(this.getClass().getSimpleName() + " -- " + "is init ------");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info(servletRequest.getContentType());
HttpServletResponse servletResponse1 = (HttpServletResponse) servletResponse;
servletResponse1.setHeader("ningtao", "test_header");
WrapperServletRequest wrapperServletRequest = null;
// 关于请求包装流的使用
if (servletRequest instanceof HttpServletRequest){
HttpServletRequest servletRequest1 = (HttpServletRequest) servletRequest;
wrapperServletRequest = new WrapperServletRequest(servletRequest1);
}
BufferedReader br = new BufferedReader(new InputStreamReader(wrapperServletRequest.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
System.out.println(line);
sb.append(line);
}
System.out.println(sb.toString());
filterChain.doFilter(wrapperServletRequest, servletResponse);
}
@Override
public void destroy() {
log.info(this.getClass().getSimpleName() + " -- " + "is destroy ------");
}
}
SpringBoot–整合–Mybatis错误:
"org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: \r\n### Error updating database. Cause: java.lang.NullPointerException\r\n### The error may exist in file [G:\code\springboot-dubbo-demo\springboot-dubbo-demo1\springboot-dubbo-provider\target\classes\mapper\EmpMapper.xml]
这个错误困扰我很久,对比了数据库中表字段和基本类字段没有问题,mapper.xml也没有问题,但是一直报这个错误,最后由重新建一个项目运行正常对比出来原因:
使用这个数据源就一直报nullpointexception:
spring.datasource.type=org.apache.ibatis.datasource.pooled.PooledDataSource
切换为另外的数据源:
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
或者阿里的druid(需要引入jar包):
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
之后正常了。。
总结
一开始一直以为是json解析有问题,最后才找到真正错误的原因。关于请求参数的整个解析流程还是不熟悉,需要继续学习:HttpMessageConverter。
关于将mysql 驱动mysql-connector-java.jar由5.xx版本升级到8.xxxx版本的time zone 问题:The server time zone value ’ xxx ’ is unrecognized or represents more than one time zone
法一:
# url加上时区 serverTimezone=GMT (或者UTC)
spring.datasource.url=jdbc:mysql://localhost:3306/nt?serverTimezone=GMT
法二:
连接上mysql,执行命令:
mysql> show variables like '%time_zone';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set, 3 warnings (0.01 sec)
mysql> set global time_zone='+8:00';
参考链接:csdn-华仔的博客