Servlet:实现动态页面的技术,看我从头到尾带你穿过servlet基础的海洋,使君有所获,有所悟

一.servlet的基本概念:

什么是servlet ?

Servlet 是一种实现动态页面的技术 . 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一 个 web app ,换句话说,servlet是 java为实现web应用提供的一些规范,而想要去参与web开发的一些软件,就要满足servlet的规范,而 这些满足servlet规范的软件我们称其为web开发的中间件(tomcat是一个常用的servlet容器),tomcat就是其中比较出色的一个,tomcat一般提供的服务如下: 在基于HTTP协议的基础上对外提供HTTP服务器,并开辟了一个端口号(8080),接收用户的请求并对其进行解析,之后将请求发送给servlet,在servlet给出响应之后,同样也是通过tomcat将请求返回给用户端。
动态页面 vs 静态页
静态页面也就是内容始终固定的页面 . 即使 用户不同 / 时间不同 / 输入的参数不同 , 页面内容也不会发生 变化. ( 除非网站的开发人员修改源代码 , 否则页面内容始终不变 ). 对应的, 动态页面指的就是 用户不同 / 时间不同 / 输入的参数不同 , 页面内容会发生变化 .
我们依然以tomcat进行举例说明:tomcat的页面是一个静态页面:

 抖音是一个动态页面:https://www.douyin.com/

 我们在加载静态页面时:一般在第一次加载完静态页面之后后面再次加载部分资源都走缓存,所以当我们在写前端项目对部分资源进行修改时,我们一定要记得清除浏览器中的缓存。

二.创建第一个servlet程序

1.创建项目

生成目录结构

2.设置编码格式

 

3.引入依赖:我们一般需要什么依赖时,直接在maven仓库中直接引入:https://mvnrepository.com/对于servlet程序而言,第一步要先引入servlet依赖。


 

4.完善目录结构:在main包下创建webapp的包,同时创建子包WEB-INF,传入web.xml的配置文件,这样一来,基本的结构就比较完整了

5.编写代码

 

6.修改打包方式并进行打包

我们在maven中进行打包, 打包结果如下:

当提示打包成功后我们查看其相关的包:我们发现打包的结果是一个jar包,但是在tomcat环境下运行需要将其打成war包,因此,我们需要修改一下打包方式:

 修改方式如下:

这时候的打包结果如下:

 

 除打包之外,我们也应该了解一下有关项目声明周期的其他指令:url如下:

Maven – Introduction to the Build Lifecycle

7.将打包的程序部署到tomcat:把生成的war包部署到tomcat中的webapps的文件夹中

 

在Windows的打开tomcat的文件是以bat结尾的文件,因此使用startup.bat运行tomcat,以.sh结尾的文件是在Linux系统下运行的,而使用关闭tomcat连接的方式是:Ctrl+v或者双击shutdown.bat 

我们发现运行时出现的提示信息均为乱码,这是因为编码集的差别,Windows环境下默认的编码方式是GBK,而我们在tomcat 中的设置的编码集是utf-8,所以在解码时出现乱码,如果想要恢复正常,则可以修改其logging.properties这个文件的部分内容:

8.测试验证程序:我们发现我们能打开tomcat的主页,这时说明tomcat启动成功了。

我们在运行tomcat之后会生成一个与war包名称一致的文件夹,我们以这个文件夹作为

 

但是如果以后都使用这种方式运行servlet,我们每次对源码进行更新都要重新打包然后部署到tomcat,操作起来非常麻烦,但是如果将tomcat导入到ideal中,直接在ideal中使用tomcat运行程序,会简单很多,所以我们直接使用tomcat server将tomcat导入到ideal中,但是很多小伙伴对这个过程比较陌生,推荐大家去看我的另一篇博客:听说你也在为配置tomcat server而烦恼,看我这一篇,让你醍醐灌顶!_努力努力再努力mlx的博客-CSDN博客

三.在运行程序时访问出现的常见错误

1.404

如果能够保证服务器能够正常运行,一般而言,出现的404错误都是由于访问路径出错了,我们可以尝试从以下这几方面进行考虑:
①没有写 contextpath

②没有写servletpath

③ servletpath 路径错误

我们拿这个路径进行说明:servlet_ljl_war_exploded是context path,index.jsp是servlet path,如果我们的程序中出现404错误,我们可以从这两个方面进行考虑。  

2 .405 方法错误

方法错误是后端提供的方法和我们访问的方法不一致,即后端是通过get方法进行请求,但是我们却使用post方法进行请求,这时则会出现405错误:方法不允许,其实出现这个错误进行异常检验也相对简单,直接对接前后端接口的方法类型即可。

3.500异常

 出现这个问题的原因主要是因为服务器内部出现错误,这时候我们需要从服务器内部进行调错。

4.出现“空白页面”

一般而言,如果页面没有给出响应,也没有报出任何错误,其实这时候大多数情况下是因为我们在后端并没有给出任何响应。

5.无法访问网页

一般是 Tomcat 启动就失败了

四.servlet中的常见类的API:
4.1HttpServelt

HttpServlet 的主要作用之一是转发请求

4.2servlet的生命周期

4.3HttpServletRequest方法

 4.3.1测试HttpservletRequest方法

package exer;

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 java.io.IOException;
import java.util.Enumeration;

/**
 * @author tongchen
 * @create 2023-04-01 15:36
 * 测试请求中的各种方法
 */
//设置注解,使浏览器可以访问
    @WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //在涉及前后端交互的过程中,最先要确定的就是在数据传输过程中的编码格式问题
        //确定传输过来数据的编码格式
        req.setCharacterEncoding("utf-8");
        //确定返回响应的编码格式
        resp.setContentType("text/html;charset=utf-8");
        //创建StringBuilder
        StringBuilder stringBuilder = new StringBuilder();
        //获取请求方法
        String method = req.getMethod();
        stringBuilder.append(method);
        //换行
        stringBuilder.append("<br>");
        //获取URL
        String requestURI = req.getRequestURI();
        stringBuilder.append(requestURI);
        stringBuilder.append("<br>");
        //获取协议和版本号
        String protocol = req.getProtocol();
        stringBuilder.append(protocol);
        stringBuilder.append("<br>");
        //获取跟路径
        String contextPath = req.getContextPath();
        stringBuilder.append(contextPath);
        stringBuilder.append("<br>");
        //获取queryString
        String queryString = req.getQueryString();
        stringBuilder.append(queryString);
        stringBuilder.append("<br>");
        stringBuilder.append("++++++++++++++++++++");
        //获取请求报头信息
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String elementKey = headerNames.nextElement();
            String elementValue = req.getHeader(elementKey);
            stringBuilder.append(elementKey+" "+ elementValue);
            stringBuilder.append("<br>");


        }
        //将全部信息写回浏览器
        resp.getWriter().write(stringBuilder.toString());

    }
}

 结果显示:

 4.4关于前后端传输过程中的乱码问题:

通常我们在数据传输到后端或者前端的时候,出现最多的问题中文数据在传输到后端或者前端时出现了乱码:但其实乱码问题只是前后端交互过程中的一个小问题,我们千万不要感觉慌张,把大问题分解为小问题,有条不紊,一步一步地将问题解决。

我们可以将数据传输过程中出现的乱码问题分为这三个阶段进行校验和处理:①浏览器将数据发送给服务器过程中出现乱码②服务器在处理数据过程中出现乱码③服务器将响应数据返回给浏览器时出现乱码:我们对此进行逐层分析即可。

我们使用以下的例子对此进行说明:

package exer;

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 java.io.IOException;
import java.text.MessageFormat;

/**
 * @author tongchen
 * @create 2023-04-01 16:29
 */
@WebServlet("/parameter")
public class GetParameter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置接收格式
     req.setCharacterEncoding("utf-8");
        String studentId = req.getParameter("studentId");
        String studentName = req.getParameter("studentName");
        String format = MessageFormat.format("studentId={0},studentName={1}", studentId, studentName);
        //在控制台打印
        System.out.println(format);
        //设置返回格式
       // resp.setContentType("text/html;charset=utf-8");
        //返回数据
        resp.getWriter().write(format);

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}


我们在服务器响应给浏览器的数据中发现studentName这一属性成了乱码,我们按照以上的三个方面对此进行分析:
 

 

从控制台中打印的代码来看的话,前端往后端传来的数据并没有出现乱码,这说明从前端往服务器传输数据没有出现乱码,而我们只是将数据进行了简单的输出,数据处理过程也不会出现乱码,所以我们将问题锁定在从服务器往前端的数据传输过程中出现了乱码,我们对此来进行分析:我们并没有对服务器到前端传输数据时规定好数据格式,因此存在由于前后端编码解码方式不一致所导致的乱码问题,因此我们需要规定好数据从后端传往前端的数据格式问题。

加入这样一条语句即可:

resp.setContentType("text/html;charset=utf-8");

我们发现了上面语句中出现了我们不了解的陌生知识点,关于text/html,这是个什么东东呢?这是规定了后端往前端传输数据的格式。

4.4.1关于前后端传输过程的数据格式问题:

关于前后端数据传输的格式,通常有以下几种:常见的type有,如text,如果是*号代表所有;

  • Text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;
  • Multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据;流的形式,常用于上传大型文件
  • Application:用于传输应用程序数据或者二进制数据;日常前后端传输数据常用。
  • Message:用于包装一个E-mail消息;
  • Image:用于传输静态图片数据;
  • Audio:用于传输音频或者音声数据;
  • Video:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。

常见的subtype有,如果是*号代表所有,用“/”与主类型type隔开;

  • text/html(以html的方式传输数据)
  • application/x-www-form-urlencoded(默认的表单方式传输数据)
  • application/json(以json的方式传输数据)
  • multipart/form-data(以文件的形式传输数据)
  • application/xml
  • text/plain
  • text/css
  • text/javascript

4.5以get请求和post请求获取参数

4.5.1以get请求获取参数

以get请求获取参数,通常是使用getParameter()这个方法来获取参数,而get请求没有请求的请求体,所以是在其的queryString中根据参数的名称来获取相应的值。

tring studentId = req.getParameter("studentId");
        String studentName = req.getParameter("studentName");
        String format = MessageFormat.format("studentId={0},studentName={1}", studentId, studentName);

HTTP协议:当下最主流的应用层协议之一,你确定不了解一下吗?_html协议_努力努力再努力mlx的博客-CSDN博客

4.5.2以post请求获取参数

①如果是通过form表单,即通过application/x-www-form-urlencoded这样的数据格式来传输数据,我们也可以使用getparameter()这个方法来获取数据,code如下:

前端:

<!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>
    <!-- servlet path 注意不要有斜杠 -->
    <form action="parameter" method="post">
        <input type="text" name="studentId" id="">
        <input type="text" name="studentName" id="">
        <input type="submit"  value="提交">
    </form>
</body>
</html>

后端:
 

package exer;

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 java.io.IOException;
import java.text.MessageFormat;

/**
 * @author tongchen
 * @create 2023-04-01 16:29
 */
@WebServlet("/parameter")
public class GetParameter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置接收格式
        //req.setCharacterEncoding("utf-8");
        String studentId = req.getParameter("studentId");
        String studentName = req.getParameter("studentName");
        String format = MessageFormat.format("studentId={0},studentName={1}", studentId, studentName);
        //在控制台打印
        System.out.println(format);
        //设置返回格式
       // resp.setContentType("text/html;charset=utf-8");
        //返回数据
        resp.getWriter().write(format);

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

测试结果:

点击提交按钮后:

 ②使用json的数据格式提交数据,然后使用第三方的jackson库来解析json字符串,其作用原理如下:在接收前端发来的请求时使用objectMapper类将json字符串转化为Java对象,在给前端发送响应时把java对象转化为json字符串发挥数据,具体code如下:

 ObjectMapper objectMapper = new ObjectMapper();
 Message message = objectMapper.readValue(req.getInputStream(), Message.class);
   String s = objectMapper.writeValueAsString(message);

 4.6HttpServletResponse类的常用api

4.6.1关于 HttpServletResponse类的常用api的几个例子:

①根据不同的输入获取不同的响应:

code如下:

package exer;

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 java.io.IOException;

/**
 * @author tongchen
 * @create 2023-04-02 16:04
 */
@WebServlet("/status")
public class GetStatus extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //首先设置两个数据格式
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //根据不同的type设置编码格式
        String type = req.getParameter("type");
        System.out.println(type);
        if(type.equals("1")){
            resp.setStatus(200);
        }
        if(type.equals("2")){
            resp.setStatus(404);
            //跳转到tomcat页面
            resp.sendError(404);
        }
        if(type.equals("3")){
            resp.setStatus(403);

        }
        if(type.equals("4")){
            resp.setStatus(500);
            
        }
//写回最终数据
        resp.getWriter().write(resp.getStatus()+" ");

    }
}


②刷新时间:

code如下:
 

package exer;

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 java.io.IOException;

/**
 * @author tongchen
 * @create 2023-04-02 16:37
 */
@WebServlet("/refresh")
public class Refresh extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求数据的编码集
        req.setCharacterEncoding("utf-8");
        //设置响应数据的格式

        resp.setContentType("application/json;charset=utf-8");
        //设置编码格式
        //测试refresh并通过结果显示变化
        resp.setHeader("refresh", "1");
        resp.getWriter().write("当前时间为"+System.currentTimeMillis());
    }
}

③重定向:

code如下: 

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 java.io.IOException;

/**
 
 */
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置跳转的目标,
        resp.sendRedirect("https://www.baidu.com");
        // 跳转之后的代码依然会执行
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
    }
}

五.实现一个简单的前后端交互的留言墙:

具体实现请大家看我另一篇博客:

六.cookie和session:
6.1基本概念:

服务器同一时刻收到的请求是很多的. 服务器需要区分清楚每个请求是从属于哪个用户, 就需要在 服务器这边记录每个用户令牌以及用户的信息的对应关系.其中这个令牌是创建session时生成的一个随机的sessionId,这个用户信息对应保存到一个session对象中,session保存到服务器中,session本身是一个map,通过随机生成的sessionId作为key,把新生成的session对象作为value,通过key-value的形式将session对象维护在服务器中

而cookie本身也是一个map,同属于一个用户的cookie通过其key-value储存在 一个session对象中,一个session对象中可能存在多个cookie,前后端交互过程中主要是通过请求报文和响应报文中的cookie和set-cookie进行维护。

cookie和session的区别:

(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中

 

6.2 关于session和cookie的常用API

6.3关于session和cookie的传输过程如下:

6.3.1简单的用户登录案例理解cookie和session

 

code如下:

前端:

<!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="login" method="post">
        <input type="text" name="user">
        <input type="text" name="password" id="">
        <input type="submit" name="submit" id="" value="提交">
    </form>
</body>
</html>

后端:

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;

/**
 * @author tongchen
 * @create 2023-04-05 16:27
 */
@WebServlet("/login")
public class Login extends HttpServlet {
    //使用dopost方法测试登录
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");
        //获取两个变量
        String user=req.getParameter("user");
        String password=req.getParameter("password");
        System.out.println("测试账号密码....." +
                user+password);
        //判断账号密码的正确情况
        if("zhangsan".equals(user)&&"123456".equals(password)){
            //
            //登录成功,设置cookie并直接跳转
            HttpSession session = req.getSession(true);
            session.setAttribute("userInfo", user);
            //跳转
            resp.sendRedirect("index");
        }else{
            //错误直接将错误写回
            resp.getWriter().write("账号密码错误");
        }
    }
}

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;

/**
 * @author tongchen
 * @create 2023-04-05 16:33
 */
@WebServlet("/index")
public class Index extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取session
        HttpSession session = req.getSession(false);
        String name =(String) session.getAttribute("userInfo");
        //将name写回
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write(name+"欢迎回来");
    }
}

 七.上传文件

前端代码:

<!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="upload" method="post" enctype="multipart/form-data">
        <input type="file" name="uploadFile" id="" >
        <input type="submit" name="" id="" value="提交">
    </form>
</body>
</html>

后端代码:

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

/**
 * @author tongchen
 * @create 2023-04-05 17:31
 */
@WebServlet("/upload")
@MultipartConfig
public class Upload extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取上传上来的文件:
        Part uploadFile = req.getPart("uploadFile");
        //测试方法
        System.out.println(uploadFile.getSubmittedFileName());
        System.out.println(uploadFile.getContentType());
        System.out.println(uploadFile.getSize());
        //存储文件
        uploadFile.write("D:/360安全浏览器下载/"+uploadFile.getSubmittedFileName());
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write("文件下载成功");

    }
}

 结果分析:


八.博客系统 

这个我后续更新~~~

猜你喜欢

转载自blog.csdn.net/m0_65431718/article/details/129870092