赞
踩
今天在写SpringBoot应用时,写一个后端生成图形验证码的业务时,需要用到session保存后端生成的图形验证码的Base64编码,然后前端通过session来显示图形验证码。
代码里用到了servlet里的HttpSession类,于是idea自动导入了javax.servlet包里的HttpSession。
业务代码如下:
- package cn.edu.wit.controller;
-
- import cn.edu.wit.entity.Result;
- import com.google.code.kaptcha.impl.DefaultKaptcha;
- import com.google.code.kaptcha.util.Config;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
- // 注意这里导入的是javax.servlet包
- import javax.servlet.http.HttpSession;
- import java.awt.image.BufferedImage;
- import java.io.ByteArrayOutputStream;
- import java.util.Base64;
- import javax.imageio.ImageIO;
-
- @Slf4j
- @RestController
- public class CaptchaController {
- private DefaultKaptcha captchaProducer;
-
- public CaptchaController() {
- // 配置Kaptcha
- captchaProducer = new DefaultKaptcha();
- captchaProducer.setConfig(new Config(new java.util.Properties()));
- }
-
- @GetMapping("/captcha")
- public Result getCaptcha(HttpSession session) throws Exception {
- // 生成验证码文本
- String captchaText = captchaProducer.createText();
- log.info("验证码: " + captchaText);
- // 将验证码文本存储在Session中
- session.setAttribute("captcha", captchaText);
-
- // 生成验证码图片
- BufferedImage image = captchaProducer.createImage(captchaText);
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- ImageIO.write(image, "jpg", outputStream);
- // 转换为Base64
- String base64Image = Base64.getEncoder().encodeToString(outputStream.toByteArray());
-
- // 返回给前端Base64编码的图形验证码
- return Result.success("data:image/jpeg;base64," + base64Image);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Postman 测试如下:
GET http://localhost:8080/captcha
启动SpringBoot应用后,测试Postman接口,idea控制台报错:
- java.lang.IllegalStateException: No primary or single unique constructor found for interface javax.servlet.http.HttpSession
- at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267)
- at org.springframework.validation.DataBinder.createObject(DataBinder.java:924)
- at org.springframework.validation.DataBinder.construct(DataBinder.java:903)
- at org.springframework.web.bind.ServletRequestDataBinder.construct(ServletRequestDataBinder.java:116)
- at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.constructAttribute(ServletModelAttributeMethodProcessor.java:156)
- at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:148)
- at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
- at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:225)
- at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:178)
- at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
- at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:917)
- at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:829)
- at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
- at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
- at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
- at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
- at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
- at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
- at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
- at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
- at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
- at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
- at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
- at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
- at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
- at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
- at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
- at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
- at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
- at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
- at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
- at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
- at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
- at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
- at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
- at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
- at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
- at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
- at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
- at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
- at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
- at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
- at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
- at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
- at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
- at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
- at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
- at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
- at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
- at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
- at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
- at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
- at java.base/java.lang.Thread.run(Thread.java:842)
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
从stackoverflow社区找到了错误原因和解决方案
错误原因原文如下:
Spring Boot 3 (and Spring Framework 6) require a baseline of Jakarte EE 10. You cannot use it with Java EE or Jakarte EE versions below that.
You have to remove the explicit dependency on
jakarta.servlet-api
from yourpom.xml
. Java Servlet 4 is below the baseline and in particular still uses the package names starting withjavax.servlet
.中文机翻:
Spring Boot 3(和 Spring Framework 6)需要 Jakarte EE 10 的基线。您不能将其与 Java EE 或低于该版本的 Jakarte EE 版本一起使用。
您必须
jakarta.servlet-api
从您的pom.xml
. Java Servlet 4 低于基线,特别是仍然使用以javax.servlet
.
也就是说:SpringBoot3已经不支持javax.servlet包里的HttpSession了,如果需要使用该类的话,需要导入jakarta.servlet包里的HttpSession。
原作者给出的解决方案如下:
If you remove the explicit dependency, Spring will pull in transitively the correct one. You then need to replace all imports starting with
javax.servlet
withjavax
replaced byjakarta
, e.g.
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse;
bug修复方法很简单,就是将所有的javax.servlet包替换成jakarta.servlet包。
例如我的代码里就应该这样修改:
- // import javax.servlet.http.HttpSession;
- // 将javax替换成jakarta
- import jakarta.servlet.http.HttpSession;
修改后,重新运行SpringBoot应用即可正常生成图形验证码,并保存到session里返回给前端。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。