zoukankan      html  css  js  c++  java
  • 补习系列(3)-springboot中的几种scope

    目标

    1. 了解HTTP 请求/响应头及常见的属性;
    2. 了解如何使用SpringBoot处理头信息 ;
    3. 了解如何使用SpringBoot处理Cookie ;
    4. 学会如何对 Session 进行读写;
    5. 了解如何在不同请求间传递 flash参数

    一、Http 头信息

    HTTP 头(Header)是一种附加内容,独立于请求内容和响应内容。
    HTTP 协议中的大量特性都通过Header信息交互来实现,比如内容编解码、缓存、连接保活等等。
    如下面的一个请求响应:
    Request

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.9
    Cache-Control: max-age=0
    Connection: keep-alive
    Host: www.cnblogs.com
    If-Modified-Since: Wed, 18 Jul 2018 13:47:45 GMT
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
    名称 用途
    Accept 客户端期望的MIME 类型列表
    Accept-Encoding 客户端期望的编解码方式
    Accept-Language 客户端期望的语言
    Cache-Control 缓存控制
    Connection 连接行为(keep-alive)
    Host 请求访问的主机
    If-Modified-Since 缓存控制
    Upgrade-Insecure-Requests 支持安全加密标记
    User-Agent 用户代理(客户端标识)

    Response

    Cache-Control: private, max-age=10
    Connection: keep-alive
    Content-Encoding: gzip
    Content-Type: text/html; charset=utf-8
    Date: Wed, 18 Jul 2018 13:47:51 GMT
    Expires: Wed, 18 Jul 2018 13:48:01 GMT
    Last-Modified: Wed, 18 Jul 2018 13:47:51 GMT
    Transfer-Encoding: chunked
    Vary: Accept-Encoding
    X-Frame-Options: SAMEORIGIN
    X-UA-Compatible: IE=10
    名称 用途
    Cache-Control 缓存控制
    Connection 连接行为(keep-alive)
    Content-Encoding 编解码方式
    Content-Type 内容类型(MIME)
    Date 当前响应时间
    Expires 文档过期时间
    Last-Modified 最后一次更新时间
    Transfer-Encoding 传输编码方式
    Vary 需要刷新的请求Header
    X-Frame-Options FRAME展示策略(用于同源控制)
    X-UA-Compatible IE兼容属性

    更多的** Http Header **可以从这里找到

    二、SpringBoot 处理头信息

    前面的内容中已经讲过如何完成Controller方法及请求的映射。
    在SpringBoot可通过@RequestHeader注解方式
    将请求头信息映射到参数,如下面的片段:

        @GetMapping("/some")
        @ResponseBody
        public String someHeader(@RequestHeader(value = "Host") String host,
                @RequestHeader(value = "User-Agent") String userAgent,
                @RequestHeader(value = "Cache-Control", required = false) String cacheControl,
                HttpServletResponse response) {
    
            logger.info("host:{}", host);
            logger.info("User-Agent:{}", userAgent);
            logger.info("Cache-Control:{}", cacheControl);
    
            // 设置响应头
            response.setHeader("Cache-Control", "no-cache,no-store,must-revalidate");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
    
            return "OK";
        }

    而响应头呢,可以通过声明一个HttpServletResponse参数后,
    通过该对象进行设置,上面的代码非常容易理解。

    如果希望获得全部的请求头,可以使用HttpHeaders对象:

        @GetMapping("/all")
        public ResponseEntity<Map<String, List<String>>> allHeaders(@RequestHeader HttpHeaders headers) {
    
            Map<String, List<String>> valueMap = new HashMap<String, List<String>>();
            for (String header : headers.keySet()) {
                valueMap.put(header, headers.get(header));
                logger.info("header[{}]={}", header, headers.get(header));
            }
    
            // 通过ResponseEntity设置响应头
            ResponseEntity<Map<String, List<String>>> entity = ResponseEntity.status(HttpStatus.OK)
                    .header("new header", UUID.randomUUID().toString()).body(valueMap);
            return entity;
        }

    上面的一段代码中,可以将所有请求头信息全部打印出来。
    此外还须注意到,返回响应使用了ResponseEntity对象,这是一个用于直接表示
    响应信息头、内容的对象,利用ResponseEntity可以很方便的设置响应头信息。

    三、Cookie处理

    Cookie一开始服务器用于辨别用户信息而记录在浏览器上的信息。
    到目前为止Cookie作为客户端的存储有了非常多的应用场景。

    SpringBoot 提供了@CookieValue以支持参数方式注入,如下:

        @GetMapping("/some")
        @ResponseBody
        public String someCookie(@CookieValue(value = "counter", defaultValue = "0") int counter,
                HttpServletResponse response) {
    
            logger.info("counter:{}", counter);
            counter += 1;
    
            String newValue = counter + "";
    
            // 设置Cookie
            response.addCookie(new Cookie("counter", newValue));
            return newValue;
        }

    上述代码中,访问/some 可以获得一个counter的cookie值,
    且每访问一次则自增一次,这是一个简单的访问计数器功能。

    如果希望获取全部的Cookie,可以参考以下代码:

        @GetMapping("/all")
        public ResponseEntity<Map<String, String>>allCookies(HttpServletRequest request, HttpServletResponse response) {
    
            Map<String, String> valueMap = new HashMap<String, String>();
            for (Cookie cookie : request.getCookies()) {
    
                valueMap.put(cookie.getName(), cookie.getValue());
                logger.info("cookie[{}]={}", cookie.getName(), cookie.getValue());
            }
    
            // 设置Cookie
            response.addCookie(new Cookie("key", UUID.randomUUID().toString()));
            return new ResponseEntity<Map<String, String>>(valueMap, HttpStatus.OK);
        }

    清理全部Cookie

        @GetMapping("/clear")
        public ResponseEntity<Map<String, String>> clearCookies(HttpServletRequest request, HttpServletResponse response) {
    
            Map<String, String> valueMap = new HashMap<String, String>();
            for (Cookie cookie : request.getCookies()) {
    
                valueMap.put(cookie.getName(), cookie.getValue());
                logger.info("cookie[{}]={}", cookie.getName(), cookie.getValue());
    
                // 清除
                cookie.setMaxAge(0);
                response.addCookie(cookie);
            }
    
            return new ResponseEntity<Map<String, String>>(valueMap, HttpStatus.OK);
        }

    Cookie机制存在一定的缺陷,尽可能在考虑一些风险后使用

    1. 安全性无法保证,除非使用HTTPS;
    2. 浏览器端只有4KB大小的存储上限;

    四、Session处理

    Session 指的是会话,是建立于Cookie机制上的一种身份识别方式。
    由于Cookie自身的安全性和容量限制,大多数应用中是在Cookie中存放一个唯一凭证;
    服务侧通过凭证再进行身份信息的存取,这就是会话的由来。
    不同的语言、框架采用的实现方式有些差异,比如JavaEE采用JSESSION_ID,而PHP则是PHPSESSID

    Session的交互原理可以参考下面一个图:

    Springboot 内嵌了Servlet容器,则是沿用的JSESSION_ID。因此在浏览一些JavaWeb站点时会发现该Cookie。
    使用@SessionAttribute可以将会话中的属性映射到方法参数;

    如果希望对Session属性进行操作,可以在Controller上声明@SessionAttributes注解以指定想要变更的属性;
    之后,通过Model参数进行写入即可(由框架自动检测并修改Session)

    @SessionAttributes("seed")
    public class SessionController {
    
        private static final Logger logger = LoggerFactory.getLogger(SessionController.class);
        @GetMapping("/some")
        @ResponseBody
        public String someSession(@SessionAttribute(value = "seed", required = false) Integer seed, Model model) {
    
            logger.info("seed:{}", seed);
            if (seed == null) {
                seed = (int) (Math.random() * 10000);
            } else {
                seed += 1;
            }
            model.addAttribute("seed", seed);
    
            return seed + "";
        }

    上面的例子与Cookie实现访问计数器的功能是一样的!
    如果希望获取全部会话,可以使用HttpSession

        @GetMapping("/all")
        public ResponseEntity<Map<String, Object>> allSessions(HttpSession session) {
    
            Map<String, Object> valueMap = new HashMap<String, Object>();
            Enumeration<String> iSession = session.getAttributeNames();
    
            while (iSession.hasMoreElements()) {
                String sessionName = iSession.nextElement();
                Object sessionValue = session.getAttribute(sessionName);
    
                valueMap.put(sessionName, sessionValue);
                logger.info("sessoin[{}]={}", sessionName, sessionValue);
            }
    
            // 写入session
            session.setAttribute("timestmap", new Date());
            return new ResponseEntity<Map<String, Object>>(valueMap, HttpStatus.OK);
        }

    清除会话

        @GetMapping("/clear")
        public ResponseEntity<Map<String, Object>> clearSessions(HttpSession session) {
    
            Map<String, Object> valueMap = new HashMap<String, Object>();
            Enumeration<String> iSession = session.getAttributeNames();
    
            while (iSession.hasMoreElements()) {
                String sessionName = iSession.nextElement();
                Object sessionValue = session.getAttribute(sessionName);
    
                valueMap.put(sessionName, sessionValue);
                logger.info("sessoin[{}]={}", sessionName, sessionValue);
                
                session.removeAttribute(sessionName);
            }
          
            return new ResponseEntity<Map<String, Object>>(valueMap, HttpStatus.OK);
        }

    五、Flash参数传递

    Flash的意思是一瞬间,一闪而过的,因此很好理解,这是一类仅用来消费一次的参数,有些类似阅后即焚
    试想这样的场景,你确认完购物车,完成订单支付后进入订单管理界面,而此时界面上提示你"下单成功,请等待发货"。
    这便可以通过Flash传参来实现。

    Flash的意义是用作请求之间的瞬时参数传递,仅消费一次后便不再用。
    以下是一个示例:

       /**
         * 执行跳转,并设置传值
         *
         * @param counter
         * @param response
         * @return
         */
        @GetMapping("/first")
        public String first(final RedirectAttributes redirectAttrs) {
    
            logger.info("redirect start:{}");
    
            redirectAttrs.addFlashAttribute("flash", UUID.randomUUID().toString());
            return "redirect:/flash/second";
        }
    
        /**
         * 获取传值
         * 
         * @param session
         * @param response
         * @return
         */
        @GetMapping("/second")
        @ResponseBody
        public String second(@ModelAttribute("flash") String flash) {
    
            logger.info("redirect receive {}", flash);
            return flash;
        }

    交互原理

    Sprintboot中Flash机制也是利用Session实现的,其中FlashMapManager接口实现了Flash参数的管理。
    默认的实现是SessionFlashMapManager,可以通过RequestContextUtils获得上下文中的FlashMapManager对象。

    RequestContextUtils通过Request Scope(请求上下文)存取对象
    这也是一个本文未提及的scope域,Request上下文是利用线程变量实现的,通常用于线程内业务处理的数据交互。

    码云同步代码

    小结

    HTTP 头信息是一种附加内容,用于实现HTTP协议中的各种特性,在开始部分介绍了常见的头信息定义。
    本文主要介绍了几种常见的HTTP scope信息的存取方法,包括如何对header、cookie进行读取及修改。
    springboot 内嵌了Servlet容器,会话处理机制上沿用了JSESSIONID,通过代码示例介绍了会话的处理方法;
    Flash参数是一种阅后即焚的数据,其底层实现也用了session的实现方案。
    欢迎继续关注"美码师的补习系列-springboot篇" ,期待更多精彩内容^-^

    作者:美码师

  • 相关阅读:
    Git将本地代码迁移到其他地方(TFS)
    vs code config
    window10 vs 以管理员打开
    Azure database
    ApplicationInsights.config导致Azure Storage 403
    sql server try catch tran
    vs code shortcut
    从备份文件bak中识别SQL Server的版本
    SQL SERVER导出模型图
    powerdesigner设计的pdm模型导出清晰图片格式
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/11861579.html
Copyright © 2011-2022 走看看