Servlet API 详解

在这里插入图片描述

文章目录

  • 前言
  • 什么是Servlet
  • HttpServlet
  • HttpServletRequest
    • 1. form表单构造POST请求
    • 2. JSON形式表示body部分
  • HttpServletResponse

前言

前面为大家介绍了如何使用 servlet 写一个简单的网站,前面只是大概了解了如何使用简单的 servlet,而平时网站的逻辑肯定不会简单,所以就需要详细的了解 servlet,servlet 作为一个 API,里面含有很多的类,不同的类中又含有不同的方法,这篇文章将为大家详细的介绍 servlet API 当中常用的类和方法。

什么是Servlet

Servlet是一种使用Java编写的服务器端程序,也称为小服务程序或服务连接器。它具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层。

从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

Servlet运行于支持Java的应用服务器中,如Tomcat、Jetty、Server等,被称为Servlet容器。Servlet容器负责调用和执行Servlet。在开发Servlet时,需要遵循Servlet规范,通过继承HttpServlet类来实现。
在这里插入图片描述

HttpServlet

我们在写 servlet 的代码的时候,类需要继承 HttpServlet 类,HttpServlet 中的主要方法有以下几种。

方法名称调用时机
init在 HttpServlet 实例化之后被调用一次
destory在 HttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求的时候调用
doGet收到 GET 请求的时候调用(由 service 方法调用)
doPost收到 POST 请求的时候调用(由 service 方法调用)
doPut/doDelete/doOptions/…收到其他请求的时候调用(由 service 方法调用)

上面的几种方法都是 HTTP 服务器自动调用的,不需要我们手动调用,我们需要做的就是重写doGet、doPost、doPut……等这些方法,将我们的代码逻辑添加进去。

既然这样的话,就会有很多人觉得这样的设定被限制了,确实这样的设定,降低了上限,但是却提升了下线,并且在实际的开发中,不要求有多么高的上限,而是需要保证下线不能很低,所以这种模式在实际开发中更常用。

当 HttpServlet 实例被创建的时候,就会自动调用 init() 方法,对实例对象进行一系列的初始化;然后当 HTTP 服务器接收到客户端的请求了之后,就会调用 service() 方法,并且在这个 service() 方法中,会调用对应的 doGet()、doPost()、doPut()……等方法;当服务器处理完请求数据并且返回响应的时候,如果没有收到请求了之后,就会调用 destory() 方法进行释放资源的操作,但是很可惜,destory() 并不是每次都能被调用到。关闭 tomcat 的方法有两种,一种是直接在资源管理器中关闭这个进程,那么这时 HttpServlet 中就来不及调用 destory() 方法,而如果是通过 8005 端口访问 tomcat 的管理端对 tomcat 下达“关闭”命令的时候,HttpServlet 中才会调用到 destory() 方法。

在这里插入图片描述

上面的描述也可以被称为 HttpServlet 的生命周期

HttpServletRequest

HttpServletRequest 表示 HTTP 请求,当 HTTP 服务器接收到客户端发送来的请求之后,会创建出 HttpServletRequest 对象,并且通过请求的数据去构造这个 HttpServletRequet 对象,在 service() 方法中调用的 doGet()、doPost()、doPut()等方法中传递的 HttpServletRequest 对象都是被处理之后的对象。

以下是 HttpServletRequest 类中的几种常用方法。

方法描述
String getProtocol()返回请求协议的名称和版本
String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分
String getContextPath()返回指示请求上下文的请求 URI 部分
String getQueryString()返回包含在路径后的请求 URL 中的查询字符串
Enumeration getParameterNames()返回一个 String 对象的枚举,包含在该请求中包含的参数的名称
String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回null
String[] getParameterValues(String name)返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null
Enumeration getHeaderNames()返回一个枚举,包含在该请求中包含的所有的头名
String getHeader(String name)以字符串形式返回指定的请求头的值
String getCharacterEncoding()返回请求主体中使用的字符编码的名称
String getContentType()返回请求主体的 MIME 类型,如果不知道类型则返回 null
int getContentLength()以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1
InputStream getInputStream()用于读取请求的 body 内容. 返回一个 InputStream 对象

下面我将用一些代码来展示这些方法的使用。

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;@WebServlet("/httpservletreq")
public class HttpServletreq extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {StringBuilder sb = new StringBuilder();sb.append(req.getProtocol()); //返回协议名称和版本sb.append("<br>");sb.append(req.getMethod()); //返回HTTP请求的方法sb.append("<br>");sb.append(req.getRequestURI()); //返回从协议名开始直到HTTP请求中的query stringsb.append("<br>");sb.append(req.getContextPath()); //返回HTTP请求的URI部分sb.append("<br>");sb.append(req.getQueryString()); //返回请求URL中的query string部分sb.append("<br>");Enumeration<String> parameter = req.getParameterNames(); //返回URL中query string的所有keywhile (parameter.hasMoreElements()) {String key = (String)parameter.nextElement(); //获取query string的指定key的valueString value = req.getParameter(key);sb.append(key + ": " + value + "<br>");}Enumeration<String> enumeration = req.getHeaderNames(); //返回请求Header中的所有keywhile (enumeration.hasMoreElements()) {String key = enumeration.nextElement(); //获取Header中指定key的valueString value = req.getHeader(key);sb.append(key + ": " + value + "<br>");}sb.append(req.getCharacterEncoding()); //返回请求主体使用的字符编码sb.append("<br>");sb.append(req.getContentType()); //返回请求主体的MIME类型sb.append("<br>");sb.append(req.getContentLength()); //返回请求主体body部分的长度resp.setContentType("test/html; charset=utf8"); //告诉浏览器我们返回的数据的数据类型resp.getWriter().write(sb.toString()); //将StringBuider对象中的内容返回给浏览器}
}

当写出上面的代码之后,启动这个程序,并且我们使用 postman 来构造一个 HTTP 请求。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

String[] getParameterValues(String name)往往使用在一个key有多个value的时候,比如a=1&a=2。

URL和URI的区别

  1. 含义不同:URI是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
  2. 格式不同:URL的格式一般由下列三部分组成:第一部分是协议(或称为服务方式);第二部分是存有该资源的主机IP地址(有时也包括端口号);第三部分是主机资源的具体地址。URI一般由三部分组成:访问资源的命名机制;存放资源的主机名;资源自身的名称,由路径表示。
  3. 位置不同:绝对URL(absolute URL)显示文件的完整路径,这意味着绝对URL本身所在的位置与被引用的实际文件的位置无关。绝对的URI指以scheme(后面跟着冒号)开头的URI,你可以把绝对的URI看作是以某种方式引用某种资源,而这种方式对标识符出现的环境没有依赖。

简单来说,URL 是包含于 URI 的。

这里我们构造的 HTTP 请求中没有 query string 部分,所以getQueryString() getParameterNames()返回的都是null,这里我们可以添加 query string。

在这里插入图片描述

在这里插入图片描述

除了 query string,我们还可以通过 POST 方法来通过 HTTP 请求的 body 来传递参数。

1. form表单构造POST请求

使用 POST 方法就需要在我们的程序中重写 doPost 方法。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {StringBuilder sb = new StringBuilder();sb.append(req.getProtocol()); //返回协议名称和版本sb.append("<br>");sb.append(req.getMethod()); //返回HTTP请求的方法sb.append("<br>");sb.append(req.getRequestURI()); //返回从协议名开始直到HTTP请求中的query stringsb.append("<br>");sb.append(req.getContextPath()); //返回HTTP请求的URI部分sb.append("<br>");sb.append(req.getQueryString()); //返回请求URL中的query string部分sb.append("<br>");Enumeration<String> parameter = req.getParameterNames(); //返回URL中query string的所有keywhile (parameter.hasMoreElements()) {String key = (String)parameter.nextElement(); //获取query string的指定key的valueString value = req.getParameter(key);sb.append(key + ": " + value + "<br>");}Enumeration<String> enumeration = req.getHeaderNames(); //返回请求Header中的所有keywhile (enumeration.hasMoreElements()) {String key = enumeration.nextElement(); //获取Header中指定key的valueString value = req.getHeader(key);sb.append(key + ": " + value + "<br>");}sb.append(req.getCharacterEncoding()); //返回请求主体使用的字符编码sb.append("<br>");sb.append(req.getContentType()); //返回请求主体的MIME类型sb.append("<br>");sb.append(req.getContentLength()); //返回请求主体body部分的长度resp.setContentType("test/html; charset=utf8"); //告诉浏览器我们返回的数据的数据类型resp.getWriter().write(sb.toString()); //将StringBuider对象中的内容返回给浏览器}

在这里插入图片描述

postman form 表单构造 POST 请求。
在这里插入图片描述
在这里插入图片描述

Fiddler 抓取 HTTP 请求包。

在这里插入图片描述
form 表单实际上就是将query string部分给放入到 body 部分,我们还可以使用 JSON 的方式来表示 body 部分。

2. JSON形式表示body部分

form 表单表示 body 的方法是 servlet 本身就支持的,而使用 JSON 格式则需要引入第三方库 Jackson。

在这里插入图片描述
然后选择一个版本,将坐标给导入到 maven 中。

在这里插入图片描述
jackson 中主要使用的就是一个类 ObjectMapper 和两个方法:read 方法——将 json 字符串映射为一个Java对象,write 方法——把一个Java对象映射为一个json字符串。

import com.fasterxml.jackson.databind.ObjectMapper;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;class Request {public String username;public String password;
}class Response {public boolean ok;
}
@WebServlet("/jsonpost")
public class JsonPost extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ObjectMapper objectMapper = new ObjectMapper();Request request = objectMapper.readValue(req.getInputStream(), Request.class);System.out.println("username: " + request.username);System.out.println("password: " + request.password);Response response = new Response();response.ok = true;String respJson = objectMapper.writeValueAsString(response);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}
}

Request request = objectMapper.readValue(req.getInputStream(), Request.class); 这个方法首先会执行 req.getInputStream() 方法,获取到 InputStream 流,然后 readValue() 方法会读取这个 InputStream 中的所有数据,并且将这里面的数据按照 JSON 的格式进行解析,解析成 Map(键值对)的形式;然后 readValue() 方法就会调用反射的 API,创建出 Request.class 实例,获取到 Request 类中的所有属性——username和password,然后在前面解析成的 Map 中查找 Request 中的属性,并且将查找的结果复制给对应属性。

objectMapper.writeValueAsString(response) 这个方法则是通过传入的参数获取到类对象,获取到类中的属性,然后在这个类中查找这个属性的值,并且按照 JSON 的格式,将属性和属性值构造成字符串。形如:“{ok:true}”

服务器部分代码编写完成之后,我们就来构造一个 JSON 表示body的 HTTP 请求。

在这里插入图片描述
在这里插入图片描述
Fiddler 抓取 HTTP 请求。

在这里插入图片描述

HttpServletResponse

HttpServletResponse 是服务端根据客户端发来的请求做出响应的类,HttpServletResponse中也包含了很多的方法。

方法描述
void setStatus(int sc)为该响应设置状态码
void setHeader(String name,String value)设置一个带有给定的名称和值的 header. 如果 name 已经存在,则覆盖旧的值
void addHeader(String name, String value)添加一个带有给定的名称和值的 header. 如果 name 已经存在,不覆盖旧的值, 并列添加新的键值对
void setContentType(String type)设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8
void sendRedirect(String location)使用指定的重定向位置 URL 发送临时重定向响应到客户端
PrintWriter getWriter()用于往 body 中写入文本格式数据
OutputStream getOutputStream()用于往 body 中写入二进制格式数据

setStatus(int sc) 这个方法如果不显式指出状态码的话,会默认为200。

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("/status")
public class HttpStatus extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(200);}
}

在这里插入图片描述

这里我们给相应的 Header 中设置一个刷新页面的key-value。

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("/refresh")
public class HttpRefresh extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("refresh","1000");;resp.getWriter().write(System.currentTimeMillis() + " ");}
}

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

postman 不能显示出动态变化的页面,所以我们需要在浏览器中才能看到这个变化。

在这里插入图片描述
在这里插入图片描述
当在 Header 中添加进去 refresh 键值对的时候,页面会根据后面设置的时间进行刷新。。

setContenType()setCharacterEncoding() 这两个方法作用其实是差不多的,但是我们更建议使用 setContenType() 方法,因为这个方法还可以指定字符编码集。resp.setContentType("test/html; charset=utf8")

sendRedirect(String location) 方法会设置页面重定向到哪里,也就是当状态码返回3xx的时候的重定向位置。

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 HttpRedirect extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(302);resp.setHeader("Location", "https://www.baidu.com");}
}

在这里插入图片描述

上面设置状态码为3xx,然后设置重定向位置这两个操作可以合并为一个操作。

@WebServlet("/redirect")
public class HttpRedirect extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setStatus(302);
//        resp.setHeader("Location", "https://www.baidu.com");resp.sendRedirect("http://www.baidu.com");}
}

getWriter()getOutputStream() 方法则是将响应写入 body 中,这里不过多解释。

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

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

相关文章

Hive Lateral View explode列为空时导致数据异常丢失

一、问题描述 日常工作中我们经常会遇到一些非结构化数据&#xff0c;因此常常会将Lateral View 结合explode使用&#xff0c;达到将非结构化数据转化成结构化数据的目的&#xff0c;但是该方法对应explode的内容是有非null限制的&#xff0c;否则就有可能造成数据缺失。 SE…

typora整理markdown笔记

效果 符号 快捷键 斜体 * * ctrlB(代表同时按) 加粗 ** ** ctrlI 竖线 > 超链接 清除样式 ctrl\ 图片 ![图片描述][图片绝对路径/相对路径] 如何在Typora中插入图像&#xff1f; ➊ 使用Markdown语法 &#xff08;不推荐&#xff0c;太慢&#xff09; ➋ 直接拷贝图…

Theory behind GAN

假如要生成一些人脸图&#xff0c;实际上就是想要找到一个分布&#xff0c;从这个分布内sample出来的图片像是人脸&#xff0c;分布之外生成的就不像人脸。而GAN要做的就是找到这个distribution。 在GAN之前用的是Maximum Likelihood Estimation。 Maximum Likelihood Estimat…

同创永益联合红帽打造一站式数字韧性解决方案

随着AI技术的快速兴起&#xff0c;IT技术已成为推动业务持续增长的重要驱动力&#xff0c;这要求企业不断尝试新事物&#xff0c;改变固有流程&#xff0c;加强IT技术与业务的合作&#xff0c;同时提升数字韧性能力&#xff0c;以实现业务目标。10月26日&#xff0c;红帽2023中…

Bert学习笔记(简单入门版)

目 录 一、基础架构 二、输入部分 三、预训练&#xff1a;MLMNSP 3.1 MLM&#xff1a;掩码语言模型 3.1.1 mask模型缺点 3.1.2 mask的概率问题 3.1.3 mask代码实践 3.2 NSP 四、如何微调Bert 五、如何提升BERT下游任务表现 5.1 一般做法 5.2 如何在相同领域数据中进…

学习UI第一天

在工作闲暇之余&#xff0c;自己画的原型图&#xff0c;再次做一次记录&#xff0c;哈哈哈 萌宠领养UI设计原型图 https://modao.cc/proto/lq2KqIVBs48xwylNZlA7OP/sharing?view_moderead_only #萌宠领养-分享 可以点击此链接&#xff0c;进行查看O(∩_∩)O哈哈~

二进制部署k8s集群-过程中的问题总结(接上篇的部署)

1、kube-apiserver部署过程中的问题 kube-apiserver.conf配置文件更改 2、calico的下载地址 curl https://docs.projectcalico.org/v3.20/manifests/calico.yaml -O 这里如果kubernetes的节点服务器为多网卡配置会产生报错 修改calino.yaml配置文件 解决方法&#xff1a; 调…

论文阅读——DiffusionDet

在目标检测上使用扩散模型 前向过程&#xff1a;真实框-->随机框 后向过程&#xff1a;随机框-->真实框 前向过程&#xff1a; 一般一张图片真实框的数目不同&#xff0c;填补到同一的N个框&#xff0c;填补方法可以是重复真实框&#xff0c;填补和图片大小一样的框&a…

Redis 学习

Redis 集群共3种集群模式&#xff1a; 1. 主从模式 &#xff08;主写从读&#xff09;&#xff0c;写请求分配到主库&#xff0c;完后数据同步到从库&#xff0c;从库主要负责读请求 同步过程&#xff1a; 从库启动向主库发送同步请求&#xff0c;数据传输的形式是RDB文件&am…

C++算法入门练习——树的带权路径长度

现有一棵n个结点的树&#xff08;结点编号为从0到n-1&#xff0c;根结点为0号结点&#xff09;&#xff0c;每个结点有各自的权值w。 结点的路径长度是指&#xff0c;从根结点到该结点的边数&#xff1b;结点的带权路径长度是指&#xff0c;结点权值乘以结点的路径长度&#x…

C语言-求一个整数储存在内存中的二进制中1的个数

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>int main() {/*求一个整数储存在内存中的二进制中1的个数*/int number;scanf("%d", &number);int i 0;int count 0;for (i 0; i < 32; i){if (1 ((number >> i) & 1)){count;}}printf(…

CentOS 7搭建Gitlab流程

目录 1、查询docker镜像gitlab-ce 2、拉取镜像 3、查询已下载的镜像 4、新建gitlab文件夹 5、在gitlab文件夹下新建相关文件夹 6、创建运行gitlab的容器 7、查看docker容器 8、根据Linux地址访问gitlab 9、进入docker容器&#xff0c;设置用户名的和密码 10、登录git…

卫生纸标准及鉴别

一、标准分类及含义 &#xff08;1&#xff09;标准分类 ①GB——国家强制标准&#xff08;即最低标准&#xff09; ②GB/T——国家推荐标准 ③QB——轻工行业标准 ④QB/T——轻工行业推荐标准 &#xff08;2&#xff09;含义 ①国家标准是指国家标准化主管机构批准发布的。…

适用于4×4MiMo 4G/5G,支持GNSS和WiFi 6E的车载天线解决方案

德思特Panorama智能天线致力于为用户提供在各类复杂场景中稳定供给5G、WIFI和GNSS信号的卓越性能和支持。随着5G新频段逐渐应用、WIFI 6E频率升级以及多频定位应用的普及&#xff0c;传统的BAT[G]M-7-60[-24-58]系列天线已不再适用于当前多变的环境。 然而&#xff0c;BAT天线的…

分库分表

分库&#xff0c;分表&#xff0c;分库分表 “只分库“&#xff0c;“只分表“&#xff0c;“既分库又分表" 何时分库 在面对高并发的情况下&#xff0c;数据库连接成为性能瓶颈。当数据QPS过高导致数据库连接数不足时&#xff0c;考虑分库。在读多写少的场景下&#x…

c++ list容器使用详解

list容器概念 list是一个双向链表容器&#xff0c;可高效地进行插入删除元素。 List 特点&#xff1a; list不可以随机存取元素&#xff0c;所以不支持at.(position)函数与[]操作符。可以对其迭代器执行&#xff0c;但是不能这样操作迭代器&#xff1a;it3使用时包含 #includ…

信安.网络安全.UDP协议拥塞

第一部分 如何解决UDP丢包问题 一、UDP 报文格式 每个 UDP 报文分为 UDP 报头和 UDP 数据区两部分。报头由 4 个 16 位长&#xff08;2 字节&#xff09;字段组成&#xff0c;分别说明该报文的源端口、目的端口、报文长度和校验值。UDP 报文格式如图所示。 UDP 报文中每个…

开源简历生成器OpenResume

什么是 OpenResume &#xff1f; OpenResume 是一个功能强大的开源简历生成器和简历解析器。OpenResume 的目标是为每个人提供免费的现代专业简历设计&#xff0c;让任何人都能充满信心地申请工作。 OpenResume 有 5 个核心特点&#xff1a; 特征描述1. 实时UI更新当您输入简历…

技术实践|高斯集群服务器双缺省网关故障分析

导语&#xff1a;当前国产化数据库使用范围越来越广泛&#xff0c;在GaussDB数据库的使用过程中难免会遇到一些问题&#xff0c;有的问题是由于在安装过程中没有注意细节而产生的&#xff0c;多数隐患问题都是在特定场景下才会暴露出来&#xff0c;且暴露的时间未知&#xff0c…

2023年电子工程师大会暨第三届社区年度颁奖活动--【其利天下技术】

华秋电子发烧友将于2023年11月23日在深圳举办一场盛大的技术交流活动&#xff0c;即“2023年电子工程师大会暨第三届社区年度颁奖活动”。本次活动邀请了各大高校教授、企业高管、行业专家和电子工程师们齐聚一堂&#xff0c;围绕“开源硬件”、“OpenHarmony RISC-V”、“工程…