161_SpringBoot_web_视图解析_HandlerInterceptor_文件上传_异常处理_原生组件注入
【摘要】 视图解析_HandlerInterceptor_文件上传_异常处理_原生组件注入
视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染
1 视图解析器
2 视图解析原理流程
1 目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2 方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
3 任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
4 processDispatchResult 处理派发结果(页面改如何响应)
1 render(mv, request, response); 进行页面渲染逻辑
1 根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】
1 所有的视图解析器尝试是否能根据当前返回值得到View对象
2 得到了 redirect:/main.html --> Thymeleaf new RedirectView()
3 ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
4 view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
RedirectView 如何渲染【重定向到一个页面】
1 获取目标url地址
2 response.sendRedirect(encodedURL);
视图解析:
返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);
返回值以 redirect: 开始: new RedirectView() --> render就是重定向
返回值是普通字符串: new ThymeleafView()--->
样例代码
@Slf4j
@Controller
public class IndexController {
/**
* 来登录页
* @return
*/
@GetMapping(value = {"/","/login"})
public String loginPage(){
return "login";
}
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){ //RedirectAttributes
if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陆成功的用户保存起来
session.setAttribute("loginUser",user);
//登录成功重定向到main.html; 重定向防止表单重复提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","账号密码错误");
//回到登录页面
return "login";
}
}}
2 拦截器
HandlerInterceptor 接口
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}" ,requestURI);
// LoginInterceptor.log.info("preHandle拦截的请求路径是{}",requestURI);
// log.info();
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser != null){
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
配置拦截器
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**", "/js/**","/aa/**"); //放行的请求
}
}
拦截器原理
1 根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2 先来顺序执行 所有拦截器的 preHandle方法
a 如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
b 如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3 如果任何一个拦截器返回false。直接跳出不执行目标方法
4 所有拦截器都返回True。执行目标方法
5 倒序执行所有拦截器的postHandle方法。
6 前面的步骤有任何异常都会直接倒序触发 afterCompletion
7 页面成功渲染完成以后,也会倒序触发 afterCompletion
文件上传 文件自动配置原理
1 文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
2 自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
3 原理步骤
1 请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
2 参数解析器来解析请求中的文件内容封装成MultipartFile
3 将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>
FileCopyUtils。实现文件流的拷贝
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">名字</label>
<input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">头像</label>
<input type="file" name="headerImg" id="exampleInputFile">
</div>
<div class="form-group">
<label for="exampleInputFile">生活照</label>
<input type="file" name="photos" multiple> <!-- multiple 上传多个文件-->
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
@Slf4j
@Controller
public class FormTestController {
@GetMapping("/form_layouts")
public String form_layouts(){
return "form/form_layouts";
}
/**
* 参数用 @RequestPart
* MultipartFile 自动封装上传过来的文件
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上传的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
}
异常处理 错误处理, 默认规则
默认情况下,Spring Boot提供/error处理所有错误的映射
对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。
对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
自定义错误页 有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页
error/404.html
error/5xx.html
Web原生组件注入(Servlet、Filter、Listener)
1 原生方式使用Servlet API
@ServletComponentScan(basePackages = "com.alex") :指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器?
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
@ServletComponentScan(basePackages = "com.alex")
@SpringBootApplication
public class SpringbootWebadminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebadminApplication.class, args);
}
}
@WebServlet (urlPatterns = "/alex")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("alex");
}
}
2 推荐使用 RegistrationBean
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean
//@WebServlet (urlPatterns = "/alex")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("alex");
}
}
@Slf4j
//@WebFilter(urlPatterns={"/css/*","/images/*"}) //my
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyFilter初始化完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("MyFilter工作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
log.info("MyFilter销毁");
}
}
@Slf4j
public class MySwervletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("MySwervletContextListener监听到项目初始化完成");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("MySwervletContextListener监听到项目销毁");
}
}
@Configuration(proxyBeanMethods = true)
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
return new ServletListenerRegistrationBean(mySwervletContextListener);
}
}
嵌入式Servlet容器
原理
1 SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat
2 web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext
3 ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂---> Servlet 的web服务器)
4 SpringBoot底层默认有很多的WebServer工厂;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
5 底层直接会有一个自动配置类。ServletWebServerFactoryAutoConfiguration
6 ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)
7 ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory
8 TomcatServletWebServerFactory 创建出Tomcat服务器并启动;TomcatWebServer 的构造器拥有初始化方法initialize---this.tomcat.start();
9 内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)