Servlet 说明
首先我们需要了解浏览器访问服务器 我们这里还是以tomcat作为服务器参考对象。
①:Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有的HTTP头数据读可以通过request对象调用对应的方法查询到。
②:Tomcat同时会要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器
就那我么部署的项目来说。我们开启服务后。然后我们通过一段链接来访问我们的服务器然后得到响应数据。
http://localhost/login.html
localhost后面还有个80这里省略了。 我们可以根据localhost:80端口号找到tomcat服务器,然后后面的路径分别指定了访问项目路径和访问的内容。
我们在访问tomcat的时候会有一个默认的servlet数据处理。
我们也可以在web.xml查看内容。
我们没有创建Servlet类,里面的一些处理以及默认参数都是按照服务端默认数据进行,我们需要大致了解这些过程。
另外我们需要了解到如果我们在java中运用这个技术,就可以做出动态web开发。
Servlet初步入门尝试
我们运用maven在idea里面添加Servlet依赖坐标
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
然后我们创建一个类,来实现Servlet接口,并重写接口当中所有方法。
package jgd;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo1")
public class ServletDemo01 implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet hello world");
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
上面的注解说明了访问路径。
启动! 对的什么也没有,我们来看控制台。 我们执行后,service方法被调用了。
Servlet由web服务器创建,Servlet方法由web服务器调用 我们自定义的Servlet,必须实现Servlet接口并复写其方法,而Servlet接口中有service方法 ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用 service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互
Servlet生命周期
在Java中对象的生命周期指一个对象从被创建到被销毁的整个过程。 我们可以将Servlet大致划分为四个阶段
1:加载和实例化,我们的默认情况下,没有自己指定值的话,Servlet在被第一次被访问的时候,会由容器创建Servlet对象。
默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置? @WebServlet(urlPatterns = “/demo1”,loadOnStartup = 1) loadOnstartup的取值有两类情况 (1)负整数:第一次访问时创建Servlet对象 (2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
2:初始化,继加载和实例化之后然后Servlet的init()方法会实现初始化对象。初始化完成的工作就是加载配置文件,创建连接等初始化工作。初始化的方法只会执行一次。 3:请求处理,每次请求Servlet都会自动调用service请求进行处理。 4:服务终止,当需要释放内存或者容器关闭时,容器就会调用Servlet实例的==destroy()==方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
测试用例
package jgd;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo2", loadOnStartup = 1)
public class ServletDemo02 implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet hello world");
}
public String getServletInfo() {
return null;
}
public void destroy() {
System.out.println("destory---");
}
}
Servlet方法说明和体系结构
方法说明
在上文中已经说明了三个方法。初始化,服务,以及销毁。 1:初始化方法,只执行一次,默认在Servlet被第一次访问执行。
init(ServletConfig servletConfig)
2:服务方法,每次Servlet被访问的时候会默认访问,主要用于编写处理的访问逻辑.
void service(ServletRequest servletRequest, ServletResponse servletResponse)
3:销毁方法,Servlet被销毁的时候,该方法被调用。在内存释放或者服务器关闭的时候销毁Servlet。需要进行正常的关闭,如果关掉java进程就来不及等到对象被销毁。
destroy()
4:获取Servlet信息,一般的话不怎么用,给它返回空字符串和空就可以
String getServletInfo()
5:获取ServletConfig对象
ServletConfig getServletConfig()
我们研究一下如何获取到这个对象,我们可以这样做。主要是扩大作用范围。看看就明白了。
package jgd;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo3", loadOnStartup = 1)
public class ServletDemo02 implements Servlet {
private ServletConfig servletConfig;
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init");
this.servletConfig=servletConfig;
}
public ServletConfig getServletConfig() {
return servletConfig;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet hello world");
}
public String getServletInfo() {
return null;
}
public void destroy() {
System.out.println("destory---");
}
}
该对象在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可
体系结构说明
之前写的Servlet类都是来自继承,并且需要重写方法等等。
了解到的体系结构 因为我们将来开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet
所以我们来操作一下。尝试继承这个类。
package jgd;
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("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}
这里个方法分别可以代表get和post的处理逻辑。根据不同的请求方式来调用相应的方法以及做出方法提里面的处理逻辑。 在继承HttpServlet时,为什么只重写doGet和doPost
get方法我们就直接在浏览器地址栏访问到项目,如果是post我么就可以去编写一个表单,然后提交到对应web项目地址。
我们现在运行这个项目。直接访问地址。 这里成功输出了get。
然后我们写一个表单,表单的action路径要指定到你的项目路径,也就是Servlet路径。
不妨用之前那个表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
<link href="register.css" rel="stylesheet">
</head>
<body>
<div class="form-div">
<div class="reg-content">
<h1>欢迎注册</h1>
<span>已有帐号?</span> <a href="#">登录</a>
</div>
<form id="reg-form" action="/demo4" method="post">
<table>
<tr>
<td>用户名</td>
<td class="inputs">
<input name="username" type="text" id="username">
<br>
<span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span>
</td>
</tr>
<tr>
<td>密码</td>
<td class="inputs">
<input name="password" type="password" id="password">
<br>
<span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
</td>
</tr>
<tr>
<td>手机号</td>
<td class="inputs"><input name="tel" type="text" id="tel">
<br>
<span id="tel_err" class="err_msg" style="display: none">手机号格式有误</span>
</td>
</tr>
</table>
<div class="buttons">
<input value="注 册" type="submit" id="reg_btn">
</div>
<br class="clear">
</form>
</div>
<script>
var usernameInput = document.getElementById("username");
usernameInput.onblur = checkUsername;
function checkUsername() {
var username = usernameInput.value.trim();
var reg = /^\w{6,12}$/;
var flag = reg.test(username);
if (flag) {
document.getElementById("username_err").style.display = 'none';
} else {
document.getElementById("username_err").style.display = '';
}
return flag;
}
var passwordInput = document.getElementById("password");
passwordInput.onblur = checkPassword;
function checkPassword() {
var password = passwordInput.value.trim();
var reg = /^\w{6,12}$/;
var flag = reg.test(password);
if (flag) {
document.getElementById("password_err").style.display = 'none';
} else {
document.getElementById("password_err").style.display = '';
}
return flag;
}
var telInput = document.getElementById("tel");
telInput.onblur = checkTel;
function checkTel() {
var tel = telInput.value.trim();
var reg = /^[1]\d{10}$/;
var flag = reg.test(tel);
if (flag) {
document.getElementById("tel_err").style.display = 'none';
} else {
document.getElementById("tel_err").style.display = '';
}
return flag;
}
var regForm = document.getElementById("reg-form");
regForm.onsubmit = function () {
var flag = checkUsername() && checkPassword() && checkTel();
return flag;
}
</script>
</body>
</html>
注意:
启动!一定要定位到表单html。 这回我们的post请求就可以得到了。 前端发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中
一些优化封装
为了能处理不同的请求方式,我们得在service方法中进行判断,然后写不同的业务处理,这样能实现,但是每个Servlet类中都将有相似的代码,针对这个问题,有什么可以优化的策略么?
package jgd;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class ServletDemo5 implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String method = request.getMethod();
if ("GET".equals(method)) {
} else if ("POST".equals(method)) {
}
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
比如上面这段代码,我不想每次写的时候都写逻辑判断,这是一段重复的代码。我不想每次写。这里的逻辑处理还不是很多,如果很多很多的话,我们该如何如理?我们每次都需要这样处理的话,我们就应该进行封装调用。 现在我们自己定义一个类
package jgd;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class MyHttpServlet implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String method = request.getMethod();
if ("GET".equals(method)) {
doGet(servletRequest, servletResponse);
} else if ("POST".equals(method)) {
doPost(servletRequest, servletResponse);
}
}
protected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {
}
protected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
我们把方法定义好,参数安排好。可以让之后的类继承到这个类。然后实现调用统一的功能。
后面就简化了好多,这也是继承的好处。也是一种功能封装。
package jgd;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/demo6")
public class ServletDemo6 extends MyHttpServlet {
@Override
protected void doGet(ServletRequest servletRequest, ServletResponse servletResponse) {
System.out.println("get...");
}
@Override
protected void doPost(ServletRequest servletRequest, ServletResponse servletResponse) {
System.out.println("post..");
}
}
urlParrern配置
之前我们采用注解的方式配置这个访问路径,只设置了一个访问路径。其实还可以配置多个访问路径。就像这样。
@WebServlet(urlPatterns = {"/demo7","/demo07"})
其实我们这样的配置就是一种路径匹配规则或者说是项目匹配访问规则。
下面我们介绍几种匹配规则 1:精确匹配 我们在注解中这样写的话,将来我们访问的化需要通过这个完整的路径来访问项目。 eg:
@WebServlet("/jgdabc/666")
2:目录匹配 eg:
/@WebServlet(urlPatterns = "/user/*") 目录匹配
将来我们访问的时候首先需要输入user,然后后面可以跟上任何路径。
3:扩展名匹配
@WebServlet("*.do")
将来访问的时候前面可以加上任何路径,但是后面需要加上do的扩展名。
4:任意匹配
任意匹配不建议使用
- 当我们的项目中的Servlet配置了 “/”,会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet
- 当我们的项目中配置了"/*",意味着匹配任意访问路径
- DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问
测试用例
package jgd;
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("/jgdabc/666")
public class ServletDemo7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo7get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
Request 和Response 说明
简单作用说明
从单词字面意思上看,我们明白一个人是请求,一个是响应。
我们写Servlet代码的时候我们会常常看见这两个参数。
我们需要了解这两个参数器到了什么样的作用。
引用黑马的说明
request:获取请求数据
浏览器会发送HTTP请求到后台服务器[Tomcat] HTTP的请求中会包含很多请求数据[请求行+请求头+请求体] 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
response:设置响应数据
业务处理完后,后台就需要给前端返回业务处理的结果即响应数据把响应数据封装到response对象中 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果浏览器最终解析结果,把内容展示在浏览器给用户浏览
代码测试案例
package jgd;
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.BufferedReader;
import java.io.IOException;
@WebServlet("/dod/*")
public class ServletDemo8 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
System.out.println("请求方式:"+method);
String contextPath = req.getContextPath();
System.out.println("虚拟目录:"+contextPath);
StringBuffer requestURL = req.getRequestURL();
System.out.println("url"+requestURL.toString());
String requestURI = req.getRequestURI();
System.out.println("资源标识符"+requestURI);
String queryString = req.getQueryString();
System.out.println("请求参数"+queryString);
String agent = req.getHeader("user-agent");
System.out.println("浏览器"+agent);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = req.getReader();
String line = br.readLine();
System.out.println(line);
}
}
package jgd;
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("/demo9")
public class ServletDemo9 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.setHeader("content-type", "text/html;character=utf-8");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("<h1>" + name + ",欢迎你</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("POST。。。");
}
}
需要注意的地方请看代码注释
request对象是用来封装请求数据的对象 response对象是用来封装响应数据的对象
当然这只是我们一个小小的理解
Request的继承体系
然后我们还会发现在不同的Servlet中其中这两个参数的写法也是不一样的。
package jgd;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo1")
public class ServletDemo01 implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet hello world");
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
package jgd;
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("/jgdabc/666")
public class ServletDemo7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo7get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
一个是存在于继承Servlet的方法参数体中,一个是HttpServlet的doGet()和doPost()两个方法的参数体中。我们可以明显的发现这两个参数的写法是不一样的。但是其实还是一个实现请求一个实现响应。
现在我们来看Request,想必也是有自己的继承体系。
ServletRequest和HttpServletRequest都是Java提供的。然后它的实现是由Tomcat来完成的。实现类就是RequestFacade。
该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。 Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat] 来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
验证方法就是你把request打印出来,就可以理解了。
Request获取请求行数据和请求头数据
前面已经给出代码示例了。具体的作用已经在注释中标注。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
System.out.println("请求方式:"+method);
String contextPath = req.getContextPath();
System.out.println("虚拟目录:"+contextPath);
StringBuffer requestURL = req.getRequestURL();
System.out.println("url"+requestURL.toString());
String requestURI = req.getRequestURI();
System.out.println("资源标识符"+requestURI);
String queryString = req.getQueryString();
System.out.println("请求参数"+queryString);
String agent = req.getHeader("user-agent");
System.out.println("浏览器"+agent);
}
请求行包含三块内容,分别是请求方式、请求资源路径、HTTP协议及版本
对于请求头的数据,格式为key: value 其实在浏览器当中检查源代码就类似于这样的源代码检查 对比上诉代码我们根据键获取到了值。 在上面的代码示例中我们获取到的是浏览器的数据。
Request获取请求体数据
浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST
我们想要获取到提交的数据。但是我们了解到提交的数据可能是文件,也可能是纯文本数据。于是我们需要考虑到获取数据的方式。
文件当然就需要用到字节流,纯文本需要用到字符流。
获取字节数据的方法
ServletInputStream getInputStream()
获取字符数据的方法
BufferedReader getReader()
我现在就写一个表单作为提交
这是我们之前的表单,我们还用这个表单。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
<link href="register.css" rel="stylesheet">
</head>
<body>
<div class="form-div">
<div class="reg-content">
<h1>欢迎注册</h1>
<span>已有帐号?</span> <a href="#">登录</a>
</div>
<form id="reg-form" action="/dod/jgdabc" method="post">
<table>
<tr>
<td>用户名</td>
<td class="inputs">
<input name="username" type="text" id="username">
<br>
<span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span>
</td>
</tr>
<tr>
<td>密码</td>
<td class="inputs">
<input name="password" type="password" id="password">
<br>
<span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
</td>
</tr>
<tr>
<td>手机号</td>
<td class="inputs"><input name="tel" type="text" id="tel">
<br>
<span id="tel_err" class="err_msg" style="display: none">手机号格式有误</span>
</td>
</tr>
</table>
<div class="buttons">
<input value="注 册" type="submit" id="reg_btn">
</div>
<br class="clear">
</form>
</div>
<script>
var usernameInput = document.getElementById("username");
usernameInput.onblur = checkUsername;
function checkUsername() {
var username = usernameInput.value.trim();
var reg = /^\w{6,12}$/;
var flag = reg.test(username);
if (flag) {
document.getElementById("username_err").style.display = 'none';
} else {
document.getElementById("username_err").style.display = '';
}
return flag;
}
var passwordInput = document.getElementById("password");
passwordInput.onblur = checkPassword;
function checkPassword() {
var password = passwordInput.value.trim();
var reg = /^\w{6,12}$/;
var flag = reg.test(password);
if (flag) {
document.getElementById("password_err").style.display = 'none';
} else {
document.getElementById("password_err").style.display = '';
}
return flag;
}
var telInput = document.getElementById("tel");
telInput.onblur = checkTel;
function checkTel() {
var tel = telInput.value.trim();
var reg = /^[1]\d{10}$/;
var flag = reg.test(tel);
if (flag) {
document.getElementById("tel_err").style.display = 'none';
} else {
document.getElementById("tel_err").style.display = '';
}
return flag;
}
var regForm = document.getElementById("reg-form");
regForm.onsubmit = function () {
var flag = checkUsername() && checkPassword() && checkTel();
return flag;
}
</script>
</body>
</html>
这是我们的Servlet代码
package jgd;
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.BufferedReader;
import java.io.IOException;
@WebServlet("/dod/*")
public class ServletDemo8 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
System.out.println("请求方式:"+method);
String contextPath = req.getContextPath();
System.out.println("虚拟目录:"+contextPath);
StringBuffer requestURL = req.getRequestURL();
System.out.println("url"+requestURL.toString());
String requestURI = req.getRequestURI();
System.out.println("资源标识符"+requestURI);
String queryString = req.getQueryString();
System.out.println("请求参数"+queryString);
String agent = req.getHeader("user-agent");
System.out.println("浏览器"+agent);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = req.getReader();
String line = br.readLine();
System.out.println(line);
}
}
启动后项目,定位到页面。 填写信息提交后,就会提交到服务地址。
然后我们看控制台输出。 这些信息我们都获取到了。这就是我们的请求体数据。 BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁, request对象被销毁后,BufferedReader流就会自动关闭,所以此处就不需要手动关闭流了
代码的一些优化
我们主要再来看这个doGet()和doPost()两个方法 这两个方法中的参数体是一样的,参数名只是名字不同。然后处理逻辑可能有一点点差异。 但是我们是否可以提供一种统一获取请求参数的方式,来统一一下doGet和doPost方法体当中的代码。
在这之前我们再来理一下相关的方法
HTTP请求数据中包含了请求行、请求头和请求体,针对这三部分内容,Request对象都提供了对应的 API方法来获取对应的值:
请求行 getMethod()获取请求方式 getContextPath()获取项目访问路径getRequestURL()获取请求URL getRequestURI()获取请求URI getQueryString()获取GET请求方式的请求参数 请求头 getHeader(String name)根据请求头名称获取其对应的值请求体 注意: 浏览器发送的POST请求才有请求体如果是纯文本数据:getReader() 如果是字节数据如文件数据:getInputStream()
我们来演示一下getQueryString(); 我们需要在访问地址栏中填入参数。
这是我们用到的代码
package jgd;
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("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
String queryString = req.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}
这是我们启动项目后访问地址并填入参数的例子。 然后回车看输出。 下面最后一行的字段就是我们这个方法获取到的数据。我们这个叫请求数据。 于是我们归结上面的我们提出两种获取请求数据的方式。 对于请求参数的获取,常用的有以下两种: GET方式:
String getQueryString()
POST方式:
BufferedReader getReader();
针对不同的请求,我们首先可以这样。
package jgd;
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.BufferedReader;
import java.io.IOException;
@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
String queryString = req.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader reader = req.getReader();
String s = reader.readLine();
System.out.println(s);
}
}
重复了,当然你可能说一行不多,但是很有代表性的是,如果是请求方式的不同将来业务逻辑要求我们类似这样做出很多重复代码呢?这样就不是很好。
我们可以这样解决。原因在于方法体参数一样。
package jgd;
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.BufferedReader;
import java.io.IOException;
@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String params = " ";
String method = req.getMethod();
if ("GET".equals(method)) {
params = req.getQueryString();
} else if ("POST".equals(method)) {
BufferedReader reader = req.getReader();
params = reader.readLine();
}
System.out.println(params);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
但是这样的方式还需要写很多代码,我们想要寻求 一种更加简单的方式。
我们来看一些方法 获取所有参数得Map集合
Map<String,String[]> getParameterMap()
这个方法让我们获取的是Map集合类型的数据。对应键值对,键唯一,值可以多,是一个数组。
根据名称获取到值(数组)
String[] getParameterValues(String name)
根据名称获取到单个值
String getParameter(String name)
测试代码 先写一个表单
package jgd;
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.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key:map.keySet())
{
System.out.print(key+":");
String[] values = map.get(key);
for (String value:values)
System.out.println(value+" ");
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
System.out.println(password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post---");
}
}
启动! 访问到这个地址,把表单填写了。
提交以后
这是我们的一个代码举例。
我们刚刚说这是一个统一的获取的请求方式的代码。我们验证一下。我们把这些方法放到doPost里面,然后把表单的提交方式变为post。
package jgd;
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.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post---");
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key:map.keySet())
{
System.out.print(key+":");
String[] values = map.get(key);
for (String value:values)
System.out.println(value+" ");
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
System.out.println(password);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/jgdabc00" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山<br>
<input type="submit">
</form>
</body>
</html>
然后我们再次开启运行一次。看看能不能获取到数据。
在我们提交表单数据后,这里没有参数,代表了我们是按照post方式提交的。然后我们看控制台输出。
可见该段代码可以在两个方法中通用。于是我们写出一种简化的方式。
package jgd;
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.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post---");
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key:map.keySet())
{
System.out.print(key+":");
String[] values = map.get(key);
for (String value:values)
System.out.println(value+" ");
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
System.out.println(password);
}
}
可能存在的乱码的一些解决的办法
乱码的问题主要出现在tomcat8之前,但是目前的插件管理依赖只能到7。
我们还是用一个写的表单,然后写一个Servlet代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/jgdabc00" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山<br>
<input type="submit">
</form>
</body>
</html>
package jgd;
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.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post---");
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key:map.keySet())
{
System.out.print(key+":");
String[] values = map.get(key);
for (String value:values)
System.out.println(value+" ");
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
System.out.println(password);
}
}
启动! 我们填入中文,提交。
这里出现了乱码。
解决乱码
为什么会出现乱码 一句话说明就是编码和解码的方式不一样。 我们这个按照post提交的,get也一样,也会出现乱码。
解决post乱码
设置字符输入流的编码,设置的字符集要和页面保持一致
10 request.setCharacterEncoding(“UTF-8”);
我们先测试一下能否解决post乱码的问题
package jgd;
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.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post---");
req.setCharacterEncoding("UTF-8");
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key:map.keySet())
{
System.out.print(key+":");
String[] values = map.get(key);
for (String value:values)
System.out.println(value+" ");
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
System.out.println(password);
}
}
然后再次填写信息。获取数据。 成功获取到数据。
解决get请求乱码
value = new String(value.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
来看代码
package jgd;
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.nio.charset.StandardCharsets;
import java.util.Map;
@WebServlet("/jgdabc00")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get---");
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, String[]> map = req.getParameterMap();
String queryString = req.getQueryString();
System.out.println(queryString);
for (String key : map.keySet()) {
System.out.print(key + ":");
String[] values = map.get(key);
for (String value : values) {
value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
System.out.println(value);
}
}
String[] hobbies = req.getParameterValues("hobby");
String password = req.getParameter("password");
password = new String(password.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
System.out.println(password);
System.out.println(password);
}
}
一些说明
为什么web中pom的servlet依赖scope为provided
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
provided的作用就是编译的时候起作用,运行的的时候不起作用。
在我们的Tomcat里面也有自带的servlet-api.jar。如果不这样设置的话,在启动的时候会和自带的依赖产生冲突。
使用idea创建servlet
前提是我们装上上面那个项目所需的依赖。然后我们这样创建。
这是创建好的,这是默认的模板,当然我们还可以进行更改。
先打开这里 我们可以在这里修改默认的模板。 但是我习惯自己去写。简单提一下。
注:未完续更。
|