zoukankan      html  css  js  c++  java
  • SpringBoot04-web

    简介

    1. 使用SpringBoot三部曲
      1. 创建SpringBoot应用, 选中我们需要的模块.
      2. SpringBoot已经默认将这些场景配置好了, 只需要在配置文件中指定少量配置就能运行.
      3. 编写业务代码.
    2. xxxAutoConfiguration: 帮我们给容器中自动配置组件.
    3. xxxProperties: 配置类来封装配置文件的内容.

    SpringBoot对静态资源的映射规则

    1. ResourceProperties类中设置和静态资源有关的参数, 如缓存参数.
      @ConfigurationProperties(
          prefix = "spring.resources",
          ignoreUnknownFields = false
      )
      public class ResourceProperties {
          private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", 
      "classpath:/resources/", "classpath:/static/", "classpath:/public/"}; ......
    2. WebMvcAutoConfiguration类中有指出
    3. addResourceHandlers()
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
                  if (!this.resourceProperties.isAddMappings()) {
                      logger.debug("Default resource handling disabled");
                  } else {
                      Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
                      CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
                      if (!registry.hasMappingForPattern("/webjars/**")) {
                          this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"})
                             .addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"})
                              .setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                      }
      
                      String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                      if (!registry.hasMappingForPattern(staticPathPattern)) {
                          this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern})
                              .addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations()))
                              .setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                      }
                  }
              }
        1. 所有的/webjars/**访问, 都会去classpath:/META-INF/resources/webjars/目录下找资源.
          • webjars: 以jar包的方式引入静态资源
          • localhost:8080/webjars/jquery/3.3.1/jquery.js
        2. 默认静态资源路径
      "classpath:/META‐INF/resources/""classpath:/resources/", 
      "classpath:/static/""classpath:/public/"  
      "/":当前项目的根路径
        • 举例: localhost:8080/aaa.jpg  -> 会去静态资源文件夹里找aaa.jpg
    4. 欢迎页: 静态资源下所有的index.html页面, 会被/**映射
          WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, 
            Optional<Resource> welcomePage, String staticPathPattern) { if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage.get()); this.setRootViewName("forward:index.html"); } else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { logger.info("Adding welcome page template: index"); this.setRootViewName("index"); } }
      1. 如localhost:8080/ 会去找静态资源中的index.html
    5. 配置喜欢的图标: 
      1. 所有的 **/favicon.ico 都是在静态资源文件下找
    6. 当然, 我们是可以自己设置静态资源文件夹的
      spring.resources.static-locations=classpath:/hello/, classpath:/aaa/
      
      #这样, 原本默认的静态资源文件夹将不能被访问

    模板引擎

    1. 有JSP、Velocity、Freemarker、Thymeleaf.
    2. 原理
    3. SpringBoot推荐我们使用thymeleaf, 其语法更简单, 功能也更强大.
    4. 引入thymeleaf
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
    5. thymeleaf使用规则
      public class ThymeleafProperties {
          private static final Charset DEFAULT_ENCODING;
          public static final String DEFAULT_PREFIX = "classpath:/templates/";
          public static final String DEFAULT_SUFFIX = ".html";
          private boolean checkTemplate = true;
          private boolean checkTemplateLocation = true;
          private String prefix = "classpath:/templates/";
          private String suffix = ".html";
      ......
      1. 只需要我们把html页面放在classpath:/templates/中, thymeleaf就能自动渲染了.
    6. 使用
      1. 导入名称空间
        <html lang="en" xmlns:th="http://www.thymeleaf.org">
      2. 使用
        <h1>SUCCESS</h1>
            <!-- th:text 将div里的文本内容设置为指定值 -->
            <div th:text="${hello}">
                显示欢迎信息
            </div>

    thymeleaf的语法

    1. 修改标签文本值
      <!-- 服务器解析thymeleaf代码, 会读取th:text属性值, 替换原本标签体的值 -->
      <p th:text="经过服务器可见">直接在浏览器上可见</p>
    2. 修改指定属性值
      <input value="old-value" th:value="new-value">
      
      <div style="background-color: yellow" th:style="background-color: green"></div>
    3. 在表达式中访问属性域
      1. controller方法
        @Controller
        public class Test {
        
            @Autowired
            private ServletContext servletContext;
        
            @RequestMapping("/themeleaf")
            public String test(ModelMap modelMap, HttpSession session) {
            
                //存入请求域
                modelMap.addAttribute("requestScope", "xxx");
                //存入会话域
                session.setAttribute("sessionScope", "xxx");
                //存入应用域
                servletContext.setAttribute("appScope", "xxx");
        
                return "hello";
            }  
        }
      2. 页面
        <p th:text="${requestScope}"></p>
        
        <p th:text="${session.sessionScope}"></p>
        
        <p th:text="${application.appScope}"></p>
    4. 解析URL地址
      1. @{}的作用是把根路径的值附加到指定的地址前
        <p th:text="@{/aaa/bbb/ccc}"> </p>
        
        <a href="../aaa/bbb/ccc.html" th:href="@{/aaa/bbb/ccc.html}"> </a>
    5. 直接执行表达式
      1. 不在thymeleaf属性中用, 直接使用
      2. [[ ]]: 有转义效果
      3. [( )]: 无转义效果
    6. 条件判断
      <p th:if="${not #strings.isEmpty(requestScope)}">xxx</p>
      
      ~<p th:if="${#strings.isEmpty(requestScope)}">xxx</p>
    7. 遍历集合
      <!-- 使用th:each进行集合数据迭代 -->
      <!-- th:each="声明遍历: ${集合}"  -->
      <!-- th:each用在哪个标签上, 哪个标签就多次出现 -->
      <div>
          <p th:text="${str}" th:each="str: ${list}" ></p> 
      </div>
    8. 包含其他模板文件
      1. templates/aaa.html中
        <div th:fragment="Part1">
            <p>will be include content 1</p>
        </div>
      2. 主页面
        <!-- :: 左边的值拼接前后缀后必须能找到要包含的文件 -->
        <!-- :: 右边的值是代码片段的名字 -->
        <div th:insert="~{aaa :: Part1}
      3. 三种插入代码片段方式
    9. 国际化
      • ##################

    SpringMVC自动配置

    1. SpringBoot对SpringMVC提供自动配置, 是在Spring默认配置上, 添加了以下功能.
    2. 包括ContentNegotiatingViewResolver和BeanNameViewResolver.(Bean)
      1. ContentNegotiatingViewResolver执行其内部initServletContext()初始化方法. 从BeanFactoryUtils中获取全部ViewResolver.
      2. 当一个请求进来时, 调用ContentNegotiatingViewResolver下的resolveViewName()方法根据方法的返回值得到视图对象(View), 并返回bestView, 主要包括beanName参数,即对应渲染的(比如:html)文件名称.
    3. 支持提供静态资源,包括对WebJars的支持.
    4. 自动注册Converter,GenericConverter和Formatter 
      1. Converter: 转换器.
      2. Formatter: 格式化器.
    5. 支持HttpMessageConverters
      1. HttpMessageConverter: SpringMVC用来转换Http请求和响应的(User -> json)
      2. HttpMessageConverters: 是从容器中确定; 获取所有的HttpMessageConverter.
    6. 自动注册MessageCodesResolver
      1. 定义错误代码生成规则.
    7. 静态 index.html 页面支持
    8. 自定义Favicon支持.
    9. 自动使用ConfigurableWebBindingInitializer
      1. 我们可以配置一个ConfigurableWebBindingInitializer来替换默认的(要添加到容器中).

    定制web扩展配置

    1. 扩展SpringMVC: 编写一个配置类@Configuration, 要实现WebMvcConfigurer接口, 但不能标注@EnableWebMvc.
      1. 即保留了所有的自动配置, 也能用扩展配置.
        //使用WebMvcConfigurer可以扩展SpringMVC的功能
        @Configuration
        public class MyMvcConfig implements WebMvcConfigurer {
        
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                //浏览器发送xxx请求, 也会来到success页面.
                registry.addViewController("/xxx").setViewName("success");
            }
        }
    2. 全面接管SpringMVC
      1. SpringBoot对SpringMVC的自动配置不需要了, 所有的都是我们自己配. 所有的SpringMVC的自动配置全部失效了.
      2. 只需要在配置类中添加@EnableWebMvc.

     错误处理原理

    1. SpringBoot默认的错误处理机制
      1. 浏览器: 返回一个错误的页面.
      2. 其他客户端: 默认响应一个json数据.
    2. 原理: 可以参照ErrorMvcAutoConfiguration: 错误处理的自动配置.
      • 给容器中添加了以下组件
      1. DefaultErrorAttributes:
            @Override
            public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
                    boolean includeStackTrace) {
                Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
                errorAttributes.put("timestamp", new Date());
                addStatus(errorAttributes, requestAttributes);
                addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
                addPath(errorAttributes, requestAttributes);
                return errorAttributes;
            }
      2. BasicErrorController:
        @Controller
        @RequestMapping("${server.error.path:${error.path:/error}}")
        public class BasicErrorController extends AbstractErrorController {
            
            @RequestMapping(produces = "text/html")//产生html类型的数据;浏览器发送的请求来到这个方法处理
            public ModelAndView errorHtml(HttpServletRequest request,
                    HttpServletResponse response) {
                HttpStatus status = getStatus(request);
                Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                        request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
                response.setStatus(status.value());
                
                //去哪个页面作为错误页面;包含页面地址和页面内容
                ModelAndView modelAndView = resolveErrorView(request, response, status, model);
                return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
            }
        
            @RequestMapping
            @ResponseBody    //产生json数据,其他客户端来到这个方法处理;
            public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
                Map<String, Object> body = getErrorAttributes(request,
                        isIncludeStackTrace(request, MediaType.ALL));
                HttpStatus status = getStatus(request);
                return new ResponseEntity<Map<String, Object>>(body, status);
            }
      3. ErrorPageCustomizer:
        @Value("${error.path:/error}")
        
        private String path = "/error";   //系统出现错误以后来到error请求进行处理.
      4. DefaultErrorViewResolver:
        @Override
            public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
                    Map<String, Object> model) {
                ModelAndView modelAndView = resolve(String.valueOf(status), model);
                if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
                    modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
                }
                return modelAndView;
            }
        
            private ModelAndView resolve(String viewName, Map<String, Object> model) {
                //默认SpringBoot可以去找到一个页面?  如: error/404
                String errorViewName = "error/" + viewName;
                
                //模板引擎可以解析这个页面地址就用模板引擎解析
                TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                        .getProvider(errorViewName, this.applicationContext);
                if (provider != null) {
                    //模板引擎可用的情况下返回到errorViewName指定的视图地址
                    return new ModelAndView(errorViewName, model);
                }
                //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面   error/404.html
                return resolveResource(errorViewName, model);
            }
    3. 步骤
      1. 一旦系统出现4xx或5xx之类的错误, ErrorPageCustomizer就会生效.(定制错误的相应规则). 会来到/error请求, 会被BasicErrorController处理.
      2. 响应页面: 去哪个页面是由ErrorViewResolver解析得到.
        protected ModelAndView resolveErrorView(HttpServletRequest request,
              HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
            //所有的ErrorViewResolver得到ModelAndView
           for (ErrorViewResolver resolver : this.errorViewResolvers) {
              ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
              if (modelAndView != null) {
                 return modelAndView;
              }
           }
           return null;
        }

    如何定制错误响应

    • 定制错误页面
    1. 有模板引擎的情况下: error/状态码. [将错误页面命名为错误状态码.html放在模板引擎文件夹里面的error文件夹下], 发生此状态码错误时就会来到对应的页面.
      1. 我们可以使用4xx, 5xx作为错误页面的文件名来匹配这种类型的所有错误, 精准优先.
      2. 页面能够获取的信息
        • timestamp: 时间戳
        • status: 状态码
        • error: 错误提示
        • exception: 异常对象
        • message: 异常消息.
        • errors: JSR303数据校验错误都在这里.
    2. 若模板引擎找不到这个错误页面, 会去静态资源文件夹下找.
    3. 若以上都未找到错误页面, 来到SpringBoot默认的错误提示页面

    • 定制错误的json数据
    1. 第一种写法: 自定义异常处理, 返回定制json数据.
      //自定义异常
      public
      class UserNotExistException extends RuntimeException { public UserNotExistException() { super("用户不存在"); } }
      // @Controller
      public class HelloController { @ResponseBody @RequestMapping("/hello") public String hello(@RequestParam("user") String user) { if("aaa".equals(user)) { throw new UserNotExistException(); } return "Hello World"; } } //异常处理器 @ControllerAdvice public class MyExceptionHandler { @ResponseBody @ExceptionHandler(UserNotExistException.class) //捕获异常后开始处理. public Map<String, Object> handlerException(Exception e) { Map<String, Object> map = new HashMap<>(); map.put("code", "user.notexists"); map.put("message", e.getMessage()); return map; } }
      • 如果只这么写的话, 没有自适应效果, 浏览器和客户端都是json
    2. 第二种: 转发到/error进行自适应响应效果处理.
      //异常处理器
      @ControllerAdvice
      public class MyExceptionHandler {
      
          @ExceptionHandler(UserNotExistException.class)
          public String handlerException(Exception e, HttpServletRequest request) {
              Map<String, Object> map = new HashMap<>();
              //传入我们自己的错误状态码
              //否则就不会进入我们自定义的错误页面
              request.setAttribute("javax.servlet.error.status_code", 500);
              map.put("code", "user.notexists");
              map.put("message", e.getMessage());
              return "forward:/error";
          }
      }
      • 但这样写的问题是无法把我们写的数据携带出去(map中写的).
    3. 将我们的定制数据携带出去.
      1. 分析: 出现错误后, 会来到/error请求, 被BasicErrorController处理, 响应出去可以获取的数据是由getErrorAttributes()得到的. [AbstractErrorController规定的方法].
      2. 所以我们可以编写一个ErrorController的实现类, 放在容器中.
      3. 页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到的
        //异常处理器
        @ControllerAdvice
        public class MyExceptionHandler {
        
            @ExceptionHandler(UserNotExistException.class)
            public String handlerException(Exception e, HttpServletRequest request) {
                Map<String, Object> map = new HashMap<>();
                //传入我们自己的错误状态码
                //否则就不会进入我们自定义的错误页面
                request.setAttribute("javax.servlet.error.status_code", 500);
                map.put("code", "user.notexists");
                map.put("message", e.getMessage());
                request.setAttribute("ext", map);
                return "forward:/error";
            }
        }
        
        
        //给容器中加入我们自己定义的ErrorAttributes
        @Component
        public class MyErrorAttributes extends DefaultErrorAttributes {
        
            //返回的map是页面和json能获取的所有字段.
            @Override
            public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
                Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
                map.put("aaa", "bbb");
                /**
        * webRequest中封装了Request对象. * int SCOPE_REQUEST = 0; * int SCOPE_SESSION = 1; */ //异常处理器携带的数据. Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0); map.put("ext", ext); return map; } }
      4. 效果

  • 相关阅读:
    转载文章:用.NET开发MSN聊天机器人 MSN聊天机器人开发揭秘
    ppc全屏程序(转)
    表设计中遇到的多对多的关系解决方案 (zt)
    using ZedGraph
    DataTable.Select(expression)
    你认识它吗?什么是QR码?
    分享《Windows Mobile平台应用与开发》源代码
    收藏信息
    六世喇嘛仓央嘉措诗集
    使用Axis开发Web Service程序
  • 原文地址:https://www.cnblogs.com/binwenhome/p/12881581.html
Copyright © 2011-2022 走看看