文章目录
- HTTP协议
- 介绍
- 概念
- 版本
- 分类
- 特点
- 浏览器抓包观察请求报文协议
- 步骤
- 实现
- Request和Response概述
- Request对象
- Request继承体系
- Request获取请求数据
- 获取请求行数据
- 获取请求头数据
- 获取请求体数据
- 获取请求参数(通用方式)
- 解决post请求乱码问题
- 解决思路
- 解决方案
- Request请求转发(前后端分离后使用较少,面试考)
- 概述
- 实现方式
- 请求转发资源间共享数据:使用request域对象
- 请求转发特点
- Request的生命周期
HTTP协议
介绍
概念
超文本传输协议,是互联网上应用最为广泛的一种网络协议
简而言之:浏览器和服务器数据交换固定的格式
版本
http1.1:长连接,多个请求共用一个连接
分类
- 请求报文协议(浏览器发送给服务器的数据)
- 请求行
- 请求头
- 请求体
- 响应报文协议(服务器发送给浏览器的数据)
- 响应行
- 响应头
- 响应体
特点
- 先有请求
- 再有响应
- 一个请求对应一个响应
浏览器抓包观察请求报文协议
步骤
- 创建html页面
- 在html页面书写html代码
- 创建servlet
- 在servlet中书写java代码
- 启动服务器
- 打开浏览器,在浏览器访问页面,然后按F12,点击网络(network)
- 抓包结果分析
实现
- html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>GET请求</h2>
<form action="/getServlet" method="get">
用户名:<input type="text" name="username" value="zhangsan" /><br>
密码:<input type="text" name="pwd" value="123" /><br>
<input type="submit" value="提交"/>
</form>
<h2>POST请求</h2>
<form action="/postServlet" method="post">
用户名:<input type="text" name="username" value="zhangsan"/><br>
密码:<input type="text" name="pwd" value="123"/><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- java代码
package com.itheima.sh.d_http_04;
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("/getServlet")
public class GetServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get....");
}
}
package com.itheima.sh.d_http_04;
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("/postServlet")
public class PostServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post....");
}
}
- 点击网络
- 抓包结果分析
Request和Response概述
- service方法的两个参数request和response是由tomact创建的
- request表示请求数据,tomact将浏览器发送过来的请求数据解析并封装到request对象中
- servlet开发者可以通过request对象获得请求数据
- response表示响应数据,服务器发送给浏览器的数据
- servlet开发者可以通过response对象设置响应数据
request:获取请求数据
- 浏览器会发送HTTP请求到后台服务器[Tomcat]
- HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
- 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
- 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
- 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
response:设置响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
- 把响应数据封装到response对象中
- 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
- 浏览器最终解析结果,把内容展示在浏览器给用户浏览
初步体验:
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象 获取请求数据
String name = request.getParameter("name");//url?name=zhangsan
//使用response对象 设置响应数据
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post...");
}
}
浏览器访问:
Request对象
Request继承体系
从java ee-api中可以查看到Servlet和HttpServletRequest两个都是接口,接口是无法创建对象的,那么service和doGet还有doPost方法中参数的对象是由谁创建的呢?
这个时候,我们就需要用到Request继承体系中的RequestFacade
:
- 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
- Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
- 要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法
Request获取请求数据
HTTP请求数据总共分为三部分内容,分别是请求行,请求头,请求体
获取请求行数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求方式: GET String getMethod()
System.out.println(request.getMethod());
//2.获取虚拟目录(项目访问路径): String getContextPath() 就是我们在tomcat配置的位置设置的路径,是虚拟的,就是随便定义
//如果设置虚拟路径位置是/那么该方法获取的内容是空字符串
System.out.println(request.getContextPath());//""
//3.获取URL(统一资源定位符): http://localhost:8080/request-demo/req1
//StringBuffer getRequestURL() 获取的是请求资源的绝对路径(含三要素)
//StringBuilder 和 StringBuffer都表示字符串缓冲区,字符可以改变 。StringBuilder多线程不安全 效率高
//StringBuffer多线程安全 效率低
StringBuffer sb = request.getRequestURL();
String s = sb.toString();
// http://localhost:8080/day16/requestRowDemo01Servlet
System.out.println("s = " + s);
//4.获取URI(统一资源标识符): /request-demo/req1
//String getRequestURI() 获取的是请求资源的绝对路径(不含三要素)
// /day16/requestRowDemo01Servlet
System.out.println(request.getRequestURI());
//5.获取请求参数(GET方式): username=zhangsan&password=123 了解
//String getQueryString()
System.out.println(request.getQueryString());//"username=suoge&pwd=1234"
}
获取请求头数据
请求头的数据,格式为key:value
所以可以根据请求头名称获取对应值的方法:
获取请求体数据
浏览器在发送get请求的时候是没有请求体的,所以需要把请求方式变更为post
获取请求体数据的方法:
1)ServletInputStream getInputStream()该方法可以获取字节和字符数据
2)BufferedReader getReader() 获取字符数据
注:BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,request对象被销毁后,BufferedReader流就会自动关闭,所以此处就不需要手动关闭流了。
获取请求参数(通用方式)
- 就是在获取之前获取请求方式,然后判断get还是post
- 通用方式,不需要判断请求方式(建议)
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/req4" method="get">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="checkbox" name="hobby" value="1"> 游泳
<input type="checkbox" name="hobby" value="2"> 爬山 <br>
<input type="submit">
</form>
</body>
</html>
java代码:
package com.example.resquest;
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;
import java.util.Arrays;
import java.util.Map;
import java.util.function.BiConsumer;
@WebServlet("/req4")
public class RequestDemo4Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
获取请求参数的通用方式:
1.Map<String,String[]> getParameterMap() 获取所有的请求参数。
抓包冒号左边的作为map集合的key,冒号右边的值作为map集合的value
2.String[] getParameterValues(String name) 获取指定的值。参数是抓包冒号左边的标识。获取的值放到数组中
3.String getParameter(String name)获取指定的值。 参数是抓包冒号左边的标识
注意:如果参数的name有多个值谁在前面先获取谁
抓包结果:
username: zhangsan
password: 1234
hobby: 1
hobby: 2
*/
//获取所有的请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历所有的请求参数
parameterMap.forEach((key,value)-> System.out.println(key+"---"+Arrays.toString(value)));
//获取hobby的值
String[] hobbies = request.getParameterValues("hobby");
System.out.println("hobby:"+Arrays.toString(hobbies));
//获取username的值
String username = request.getParameter("username");
System.out.println(username);
}
}
jdk8遍历map集合:
map.forEach(new BiConsumer<String, String[]>() {
@Override
public void accept(String s, String[] strings) {
}
});
BiConsumer中accept为抽象方法,就有一个,所以未函数式接口,能用lambda表达式简写
解决post请求乱码问题
解决思路
- 从tomact8开始以后,对于get请求乱码,tomact已经解决。对于post请求中文乱码没有解决,需要我们字节处理
- post请求乱码产生的原因和解决思路
说明:
- 页面使用的编码表是UTF-8编码,tomact使用的默认编码表ISO-8859-1进行解码,编码和解码使用的编码表不一样导致乱码
- 解决思路:先按照ISO-8859-1编码,再按照UTF-8进行重新解码
解决方案
方案一:
使用URLEncoder类进行编码:static String encode(String s, String enc)
参数:
s:编码的字符串
enc:使用编码表
使用URLDecoder进行解码:static String decode(String s, String enc)
参数:
s:解码的字符串
enc:使用编码表
方案二:
使用String类中的方法进行编码: byte[] getBytes(String charsetName)
参数表示指定的编码表,返回值表示编码后的字节数组
使用String类中的构造方法进行解码:String(byte[] bytes, String charsetName)
参数:
bytes:字节数组
charsetName:表示指定的编码表
返回值:解码后的字符串
方案三:
如果是get请求,tomcat8底层已经帮助我们解决完了,我们只需要解决post乱码即可,但是上述
两种方式对于post请求可以解决乱码,对于get请求本身获取到的已经是正确的数据,处理
后又乱码了。
我们的想法是:get请求不用我们自己书写代码处理乱码,只需要我们书写代码处理post乱码。
我们接下来学习第三种解决方案:
只解决来自于请求体数据的乱码。而get请求体没有数据,post请求体含有数据,所以我们可以理解为第三种处理方案只
是用来解决post乱码的。使用的api是ServletRequest接口中的:
void setCharacterEncoding(String env)
参数:指定的编码表
注意:该方式的代码必须书写在获取请求数据之前
Request请求转发(前后端分离后使用较少,面试考)
概述
一种在服务器内部的资源跳转方式
- 浏览器发送请求给服务器,服务器中对应的资源A接受到请求
- 资源A处理完请求后将请求发给资源B
- 资源B处理完后将结果响应给浏览器
- 请求从资源A到资源B的过程就叫请求转发
实现方式
req.getRequestDispatcher(“资源B路径”).forward(req,resp);
说明:
1)获取这转发器:RequestDispatcher dispatcher = req.getRequestDispatcher(“资源B路径”);
2)转发:RequestDispatcher表示转发器,该接口中有一个方法:forward(request,response)
请求转发资源间共享数据:使用request域对象
- 存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
- 根据key获取值
Object getAttribute(String name);
- 根据key删除改键值对
void removeAttribute(String name);
请求转发特点
- 地址栏不会发生改变
- 可以共享request域对象数据
- 一次请求一次响应
- 属于服务器内部跳转行为
Request的生命周期
- 何时创建?
浏览器第一次访问tomcat服务器的时候
- 谁创建
tomact
- 创建对象做什么
浏览器第一次访问tomcat服务器的时候,tomcat创建request对象和response对象,传递给servlet中的service方法,然后我们可以在servlet中使用request对象调用方法获取请求数据(请求行 头 体),然后处理业务逻辑,处理完毕,然后tomcat将响应数据给浏览器,浏览器接收到响应之后,tomcat立刻销毁request和response对象。