异常排查 | 重复Cookie访问导致HTTP请求引发空指针异常

news2024/11/24 18:53:58

在这里插入图片描述

文章目录

  • 一、场景描述
  • 二、异常说明
  • 三、查找问题
  • 四、调试排查
  • 五、思考分析
  • 六、解决方案
  • 七、写在最后

近几日,遇到一个困惑了我很久的异常,是浏览器页面向Tomcat服务器发起HTTP请求时,服务器发还回来的一处异常

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "javax.servlet.ServletContext.getAttribute(String)" is null

一、场景描述

首先来说一下我是在做什么的过程中遇到这个问题

  • 现在我需要实现一个监听器,去监听在线用户人数,也去实时记录一下当前这个页面中有多少用户在线,这一块的话需要使用到Servlet三大组件之一的【监听器】,我使用的是HttpSession这个接口,然后通过重写内部的sessionCreated()sessionDestroyed()方法去判定当前页面的用户接入与离开
  • 当我开启Tomcat服务器,然后打开三个浏览器去进行测试的时候,是没有问题的,只要访问一次在线人数就 + 1

在这里插入图片描述

不过呢,当然在重新启动Smart Tomcat的时候,就出现了下面的情况,请看【问题描述】~

二、异常说明

  • 首先我们来看看这个异常,很显眼的几个大字可以看出HTTP状态500 - 内部服务器错误,这是HTTP的一个状态码,服务器发还回这个状态的话表示是【我们在后端的代码出问题了,异常实在代码中抛出的】

在这里插入图片描述

  • 但是光看状态码可不行,我们还要精准地定位到是哪个地方出了问题,仔细观察可以发现,我在后端代码中使用的getAttrubute(String)获取到的内容是空的
  • 而且在下面的【例外情况】中可以看到NullPointerException空指针异常的问题,这个问题其实还挺大的,如果有看过我 指针入门到进阶教程就可以知道对于空指针来说其实是很危险的,其造成的后果堪比【野指针】
  • 不仅如此,我们还可以从服务器发还回来的响应中看到是在哪个类中哪个方法的哪一行出现了问题,所以我们就可以根据这个去进行精准定位了✌

三、查找问题

知道问题在哪了,接下去就让我们到IDEA中的查找一下后端的代码吧【文末会给出

  • 首先我们就来到这个类的方法中,然后找到指定的那一行,这个类的话是用来获取当前网页在线人数并构造HTML页面显示给用户看的,但是可以看到在这个20行的地方就已经触发了【空指针异常】,那自然下面的代码就不会执行了,此时我们应该去看看这句代码出了什么问题

在这里插入图片描述

  • 对于上面这句代码呢,就是在获取我下方这个监听器类中所统计的当前页面人数,代码逻辑我这里就不讲了,这里是在排查异常,不过Servlet的代码没有Spring Boot那么多的注解,还是好理解的
  • 那若是我们获取到的这个人数为null的话也就是表示根本没有去进行一个统计,即这个【online】压根就没有更新,那监听器这里为什么没有监听到呢?我一开始也非常纳闷这一块╮(╯▽╰)╭

在这里插入图片描述

四、调试排查

于是这个时候我只能通过调试去进行观察了

  • 首先在ServletContext这个上下文对象获取online的地方打一个断点,然后在Chrome浏览器页面前端进行用户登录点击

在这里插入图片描述

  • 点击下一步后,因为当前所获取的这个online还是为【null】,所以就代表当前这个用户是第一次接入的,此时会将这个online的值设为1

在这里插入图片描述

  • 然后在当前访问结束后,服务器便发回了这么一个页面,告知当前页面的访问者有1个

在这里插入图片描述

  • 接下去呢,我就通过IE浏览器切进行访问,一样得通过DeBug进去,可以看到此时的obj已经不为null了,所以不会进入第一个分支,而是会进入第二个if分支

在这里插入图片描述

  • 在执行完成后,我们可以在【context】中的attributes中的去找到这个online观察,就可以看到它的值为2,这也就是意味着当前这个网页的访问者有2位了

在这里插入图片描述

  • 然后呢,我再通过Edge浏览器去进行访问,此时可以看到获取到的online值为2,表明此时已经有2个人在线了,那么当这个用户再接入的时候,online的值就会变为3

在这里插入图片描述

  • 然后继续下去就可以看到这个online确实变为3了,也就表示此时有3个人正在访问这个页面

在这里插入图片描述
在这里插入图片描述

五、思考分析

但此时当我将Tomcat去进行一个重启后,再进行访问的话,就会造成下面这样的错误

在这里插入图片描述

  • 于是我就通过这个【服务器重启】来推断到底是出了什么问题,以下是我的思考:

🤔因为服务器这边重启了,所以之前保存的用户信息都不见了,但是呢这个时候客户端却不知道,还是以它之前的那个状态在进行访问,因为服务器之前在第一次访问的时候已经给了每个连接进来的用户创建了一个会话,并且生成了一个SessionId,分别代表了它们各自的身份,而每个SessionId又是存放在浏览器Cookie中的,因为前后端这里的逻辑就对接不上了,服务器完全不认识这个人,所以便返回给它一个【内部服务器错误】👈

所以经过我上面的一番思考,便想到了可能和浏览器这边的Cookie有点关系,于是我就去翻找了每个浏览器的Cookie

  • 于是,我就发现了这个网页所在的Cookie,似乎就明白了一些东西😮因为当前这个浏览器的这个页面已经拥有了一份服务器所生成的SessionId,就保存在当前的这份【Cookie】中,于是当服务器重启后再度去进行访问的时候,那Tomcat这只猫🐱便说:“你谁呀,我不认识你呀”
    在这里插入图片描述那么问题其实就是出在这个Cookie上,那一定有同学会问:这该怎么解决呢?

在这里插入图片描述

六、解决方案

  • 我这里只想到了两种解决方法:
  1. 一个就是在当前网页中加一个【退出】按钮,然后让用户想要退出的时候就惦记这个按钮,此时不仅仅是宣告这名用户的退出,而且要将其在浏览器中所保存的Cookie也随之销毁,这个的话你可以通过前端三剑客中的JavaScript来进行完成,这里暂不做介绍,后续有机会更新
  2. 第二种办法就是我正在使用的,那就是在后端这里做一下判断,我们自己去拦截一下这个空指针异常,而不是让服务器将此错误发还给用户
  • 看到代码,我没有直接用count去接受强转后的在线人数,而是先通过一个【Object】父类去接收一下这个HttpSession对象,因为这个对象也是一个键值对的形式,是我们程序员自己构造的,因此其返回值类型就要是一个总的父类,可以接受任何种类的内容
  • 若是这个对象中的内容为空的话,此时表明触发了空指针异常,那么我们就要自己捕获一下,然后告知用户他此前登录过,需要手动清除一下浏览器缓存才可以继续登录(当然这是模拟登录,现实中不可能这样)
Object obj = req.getServletContext().getAttribute("online");
if (obj == null){
    System.out.println("当前用户此前登录过");
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write("<h4><u>当前用户此前登录过,请清除浏览器缓存后再行登录</u></h4>");
}
int count = (int)obj;
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("在线人数为:" + count);
  • 可以看到若是我此时再去重启一下服务器,用一个已经接进来过的用户再次进入的话,就会构造出如下这样一个页面去提示用户先清除一下当前浏览器的缓存,然后再登录

在这里插入图片描述

但是,浏览器缓存要怎么清理呢?

温馨提示:每个浏览器都是不一样的,读者可自己试着摸索

在这里插入图片描述


然后展示一下整体代码,可供测试

前端登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="onlineUsers", method="post">
        <label>用户名</label>
        <input type="text" name="userName">
        <input type="submit" value="登录">
    </form>
</body>
</html>

监听器实现类

import javax.servlet.ServletContext;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class UsersCount implements HttpSessionListener{
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        System.out.println("会话创建");
        // 1.获取ServletContext对象,在整个容器中只有一份
        ServletContext servletContext = event.getSession().getServletContext();
        System.out.println(event.getSession().getId());

        // 2.在线人数
        Object obj = servletContext.getAttribute("online");
        // 3.判断当前用户是否是第一个在线的
        if(obj == null){
            servletContext.setAttribute("online", 1);
        }else{
            int i = (int)obj;             // 若其不是第一个上线的,就 + 1
            servletContext.setAttribute("online", i + 1);
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.println("会话销毁");
        event.getSession().invalidate();
        // 1.获取ServletContext对象,在整个容器中只有一份
        ServletContext servletContext = event.getSession().getServletContext();
        // 2.在线人数
        Object obj = servletContext.getAttribute("online");

        if(obj == null){
            servletContext.setAttribute("online", 0);
        }else{
            int i = (int)obj;             // 若其不是第一个上线的,就 + 1
            servletContext.setAttribute("online", i - 1);
        }
    }
}

登录验证的Servlet类

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/onlineUsers")
public class loginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("userName");
        if(!userName.equals("zhangsan")){
            resp.sendRedirect("login.html");
        }

        HttpSession httpSession = req.getSession(true);
        httpSession.setAttribute("userName", userName);
        resp.sendRedirect("show");
    }
}

展示结果处理是Servlet类

@WebServlet("/show")
public class showServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession httpSession = req.getSession(false);
        if(httpSession == null){
            System.out.println("未登录,请重新登录");
            resp.sendRedirect("login.html");
            return;
        }

        Object obj = req.getServletContext().getAttribute("online");
        if (obj == null){
            System.out.println("当前用户此前登录过");
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().write("<h4><u>当前用户此前登录过,请清除浏览器缓存后再行登录</u></h4>");
        }
        int count = (int)obj;
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("在线人数为:" + count);
    }
}

七、写在最后

以上就是本文所排查的问题,你学会了吗?我们来回顾一下

  • 对于这种服务器返还的HTTP响应状态码,向来是很多同学都很苦恼的东西,大家都喜欢看到自己程序正常的样子,但是却不希望看到自己的程序出问题,出一堆Bug,然后面对满屏的报错两眼一瞪👀手足无措
  • 这其实对于学习编程来说是非常不好的,跟着我这么一步步地调试、排查、分析下来,相信你一定有了自己的理解,下次在面对遇到的问题时就不会毫无办法,也可以学着自己去调试分析,找出问题所在🔍
  • 虽然这很消耗时间,而且又要动脑,但是总比你用眼睛盯着代码看一个小时毫无动作要来得好吧,所以行动起来吧,同学们!

2023年5月25日晚8点记

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/571593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html实现酷炫星空可视化大屏(附源码)

文章目录 1.设计来源1.1 可视化架构1.2 可视化大屏界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130884793 html实现酷炫星空可视化大屏(附源码) &#xff0c;html大…

华为OD机试真题(Java),跳跃游戏 II(100%通过+复盘思路)

一、题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i]0i j < 返回到达 num…

Talk预告 | ICML‘23 Oral 字节跳动 AI Lab 研究员郑在翔:人工智能如何助力蛋白质设计?

本期为TechBeat人工智能社区第500期线上Talk&#xff01; 北京时间5月25日(周四)20:00&#xff0c;字节跳动 AI Lab 研究员 — 郑在翔的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “人工智能如何助力蛋白质设计 ”&#xff0c;届时将介绍基于…

springboot+vue智慧食堂系统(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智慧食堂系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

Rancher添加集群报错:Etcd Cluster is not healthy

原因&#xff1a; 有一台虚拟机在升级内核失败后&#xff0c;回滚至快照。但由于快照版本太老旧&#xff0c;和当前的rancher版本不匹配&#xff0c;服务器上的agent等需要清楚后&#xff0c;重新在rancher添加集群&#xff1b;但是只删除了rancher镜像以及agent相关容器&#…

Linux设备树:删除节点和属性的方法

[摘要]&#xff1a;本文主要介绍了在设备树中删除节点&#xff08;node&#xff09;和属性&#xff08;property&#xff09;的方法。为了便于理解&#xff0c;笔者先介绍了 dtsi 和 dts 的关系&#xff0c;然后构建了虚拟的需求场景&#xff0c;最终给出示例。 背景知识 设备…

易基因:MeRIP-seq等揭示m6A reader YTHDF1在结直肠癌PD-1免疫治疗中的作|Gut

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 结直肠癌&#xff08;colorectal cancer &#xff0c;CRC&#xff09;是全球最常见的癌症之一&#xff0c;转移性CRC患者的5年生存率低于20%。免疫检查点阻断&#xff08;Immune checkpo…

基于LLMs的多模态大模型(MiniGPT-4,LLaVA,mPLUG-Owl,InstuctBLIP,X-LLM)

这个系列的前一些文章有&#xff1a; 基于LLMs的多模态大模型&#xff08;Visual ChatGPT&#xff0c;PICa&#xff0c;MM-REACT&#xff0c;MAGIC&#xff09;基于LLMs的多模态大模型&#xff08;Flamingo, BLIP-2&#xff0c;KOSMOS-1&#xff0c;ScienceQA&#xff09; 前…

《汇编语言》- 读书笔记 - 第3章-寄存器(内存访问):mov、add、sub、push、pop

《汇编语言》- 读书笔记 - 第3章-寄存器&#xff08;内存访问&#xff09; 3.1 内存中字的存储问题 3.1 3.2 DS 和 [address]问题 3.2 3.3 字的传送问题 3.3问题 3.4 3.4 mov、add、sub 指令3.5 数据段问题 3.53.1~3.5 小结检测点 3.1 3.6 栈3.7 CPU 提供的栈机制问题 3.6 3.8 …

基于LSB实现文本、图片、压缩包的隐藏

关于LSB的相关介绍&#xff1a; LSB全称为 Least Significant Bit&#xff08;最低有效位&#xff09;&#xff0c;是一种基于图片最低有效位修改储存信息的隐写方法&#xff0c;在CTF杂项中经常会遇到&#xff0c;LSB属于空域算法中的一种&#xff0c;是将信息嵌入到图像点中…

OPCUA从入门到精通看这里就够了

本文将会从以下几个方面介绍 1.OPCUA是什么 2.OPCUA常用的工具有那些 3.OPCUA的官网 4.使用opcua常用的方法和功能介绍 5.根据官网自己封装了一个opcuaclient类&#xff0c;并说明每个方法的用处 6.根据4中的opcuaclient类自己写了demo 本文所有用到的资料在此下载包括U…

Android平台音视频推送选RTMP还是GB28181?

技术背景 早在2015年&#xff0c;我们发布了RTMP直播推送模块&#xff0c;那时候音视频直播这块场景需求&#xff0c;还不像现在这么普遍&#xff0c;我们做这块的初衷&#xff0c;主要是为了实现移动单兵应急指挥系统的低延迟音视频数据传输。好多开发者可能会疑惑&#xff0…

信息系统建设和服务能力评估证书CS

信息系统建设和服务能力评估体系CS简介 简介&#xff1a;本标准&#xff08;团标T/CITIF 001-2019&#xff09;是信息系统建设和服务能力评估体系系列标准的第一个&#xff0c;提出了对信息系统建设和服务提供者的综合能力要求。 发证单位&#xff1a;中国电子信息行业联合会。…

整型,浮点型,大小字节知识细节一网打尽!

目录 一. 整型 2.11 C语言内置整型 2.12整型在内存如何存储&#xff1f; 2.12 原码&#xff0c;反码&#xff0c; 补码 2.13 当 整型遇上unsigned 2.1 unsigned 与 signed 解析 2.2 printf 输出 有无符号数解析 2.3 有关练习 二. 浮点型 2.51 浮点型与整型在存储上的…

视频会议产品对比分析

内网视频会议系统如何选择&#xff1f;有很多单位为了保密&#xff0c;只能使用内部网络&#xff0c;无法连接互联网&#xff0c;那些SaaS视频会议就无法使用。在内网的优秀视频会议也有很多可供选择&#xff0c;以下是几个常用的&#xff1a; 1. 宝利通&#xff1a;它支持多种…

港联证券|新产品“内卷”,史上最火光伏展能否带动光伏板块行情

在光伏成为“能源老大”之前&#xff0c;指数何时能够跑赢A股股主要股指&#xff0c;或是散户关注的重点。 时隔两年&#xff0c;SNEC上海光伏展如约举办。本届展会吸引了3000余家光伏公司参展&#xff0c;累计50万人涌进场馆&#xff0c;现场人群拥挤度丝毫不逊于过去两年资金…

Navicat连接Oracle时报错ORA-28547

错误信息 这是因为Navicat自带的oci.dll并不支持oracle11g&#xff0c;网上这么说的&#xff0c;需要去官网下载支持的版本。 直接动手。 1. 先DBeaver的连接到oracle数据库&#xff08;为了查询版本&#xff09; 1.1 查询版本 SQL&#xff1a;select * from v$version;1.2 …

如何利用Linkage Mapper工具包评估栖息地碎片化程度详解(含实例分析)

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Linkage Mapper解密数字世界链接 文章目录 Linkage Mapper工具包评估栖息地碎片化程度详解1. 介绍1.1 简介1.2 ArcGIS概述1.3 Linkage Mapper工具包简介

生态伙伴 | 携手深圳科创学院,持续推动项目落地与成长

01 大赛介绍 中国硬件创新创客大赛始于2015年&#xff0c;由深圳华秋电子有限公司主办&#xff0c;至今已经成功举办八届&#xff0c;赛事范围覆盖华南、华东、华北三大地区&#xff0c;超10个省市区域。 大赛影响了超过45万工程师群体&#xff0c;吸引了35000多名硬创先锋报…

分享Python采集66个焦点图,总有一款适合您

分享Python采集66个焦点图&#xff0c;总有一款适合您 Python采集的66个焦点图下载链接&#xff1a;https://pan.baidu.com/s/1nHoPaUp2Bqtrs5d7J-XdwA?pwd3yfb 提取码&#xff1a;3yfb jQuery SuperSlide插件自适应banner大图焦点图片轮播切换特效 jQuerySwiper仿魅族幻灯…