161_SpringBoot_web_视图解析_HandlerInterceptor_文件上传_异常处理_原生组件注入

举报
alexsully 发表于 2021/09/26 15:39:14 2021/09/26
【摘要】 视图解析_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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200