- 1、多个路径映射一个servlet
- 2、请求路径的三种格式
- 3、serlvet的执行流程
- 4、servlet的声明周期方法
- 5、ServletContext
- 6、request
- 7、response
- 8、request和response总结
1、多个路径映射一个servlet
多个请求路径映射一个servlet:
<servlet>
<servlet-name>helloservlet</servlet-name>
<servlet-class>com.guang.servlet.HelloServlet</servlet-class>
</servlet>
<!--第一种映射,添加一个url-pattern-->
<servlet-mapping>
<servlet-name>helloservlet</servlet-name>
<url-pattern>/hello</url-pattern>
<url-pattern>/hell</url-pattern>
</servlet-mapping>
<!--第二种映射,添加一个servlet-mapping-->
<servlet-mapping>
<servlet-name>helloservlet</servlet-name>
<url-pattern>/llo</url-pattern>
</servlet-mapping>
这两种方式都可以让路径映射到具体的servlet类来进行处理对应的请求。
2、请求路径的三种格式
也就是说url-pattern的配置方式一共有三种配置:
- 完全路径匹配:以/开头
- 目录匹配:以/开头,以*结尾
- 扩展名匹配:不能够以/开头,而是以*开头的
分别举几个例子来进行说明:
完全路径匹配:上面的xml文件中配置的都是完全路径匹配
目录匹配:
<!-- 目录匹配: 以/ 开始, 以*结尾 *表示后面的内容可以有,也可以没有-->
<servlet-mapping>
<servlet-name>hello02</servlet-name>
<url-pattern>/hello02/*</url-pattern>
</servlet-mapping>
扩展名匹配:
例如: *.action; 访问: aa.action, bb.action, c.action; 错误写法: /*.do, 不可以写*.jsp,*.html
<!--后缀名匹配的方式 : 以*打头, 以后缀名结尾-->
<servlet-mapping>
<servlet-name>hello02</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
注意的地方:
-
一个路径只能对应一个servlet, 但是一个servlet可以有多个路径
-
tomcat获得匹配路径时,优先级顺序:完全路径匹配> 目录匹配 > 扩展名匹配
3、serlvet的执行流程
1、默认是在客户端第一次访问的时候,Tomcat根据客户端应用找到是哪个项目路径,然后根据url-pattern找到对应的servlet来进行处理
2、Tomcat会找到对应的servlet,然后Tomcat利用反射来创建对应的对象(单例对象);
3、在创建完成对象之后,会去调用init()方法来完成对该servlet的初始化操作;
4、Tomcat会针对每次到达的请求,都会从底层的线程池中获取得到新的线程,然后调用该servlet的service方法来进行处理;
5、在容器关闭阶段,会调用destroy方法来完成对servlet的销毁;
4、servlet的声明周期方法
一共有是三个,在继承了Servlet接口中,五个方法中,有三个是生命周期方法:
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
其中:
void init(ServletConfig var1) throws ServletException;
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
void destroy();
-
常规【重点】
(1):默认情况下, 来了第一次请求, 会调用init()方法进行初始化【调用一次】
**作用**:用于初始化.. 准备工作(创建流对象 | 准备数据库连接..)
(2):任何一次请求 都会调用service()方法处理这个请求
**作用**:用于接收请求、处理请求
(3):服务器正常关闭或者项目从服务器移除, 调用destory()方法进行销毁【调用一次】
**作用**:用于处理收尾的工作。 关流 | 释放连接
-
扩展
servlet是单例多线程的, 尽量不要在servlet里面使用全局(成员)变量,可能会导致线程不安全(如果要使用,要保证线程安全)
单例: 只有一个对象(init()调用一次, 创建一次)
多线程: 服务器会针对每次请求, 开启一个线程调用service()方法处理这个请求
因为是多线程的原因,才会导致如果使用了全局变量过程中会出现线程安全问题。
4.1、ServletConfig
Servlet的配置对象, 可以使用用ServletConfig来获得Servlet的初始化参数,(在SpringMVC里面会遇到)
注意:可以在配置中修改servlet的创建时期,默认是第一次访问的时候加载实例;我们可以通过修改servlet配置,让其提前到在容器初始化的时候就加载该实例。
对应的xml配置文件如下所示:
<!--配置ServletConfigServlet-->
<servlet>
<servlet-name>servletconfig</servlet-name>
<servlet-class>com.guang.servlet.ServletConfigServlet</servlet-class>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
<!--表示的是在容器启动的时候就开始加载当前的servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletconfig</servlet-name>
<url-pattern>/servletconfig</url-pattern>
</servlet-mapping>
对应的servlet代码是:
public class ServletConfigServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
// 获取得到serviceconfig对象
String password = servletConfig.getInitParameter("password");
System.out.println("在web.xml文件中配置的对当前servlet的初始化参数是:"+password); // 123456
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
5、ServletContext
1、servlet的应用上下文,在一个servlet项目中,有且只有一个servletcontext对象。
2、作用:
- 可以存放数据和获取数据 【使用最多】,可以给servlet共享数据
- 可以获取文件的MIME
- 获取读取文件资源【偶尔用】
- 获取全局的配置参数。
3、获取方式:只要是servlet类,都是可以得到servlet上下文对象
5.1、让多个servlet之间共享数据
对应的代码所示:
@WebServlet(name = "ServletContextOne", value = "/ServletContextOne")
public class ServletContextOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("data1",new String("hello,world"));
}
}
@WebServlet(name = "ServletContextTwo", value = "/ServletContextTwo")
public class ServletContextTwo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object hello = this.getServletContext().getAttribute("data1");
System.out.println("对应的值是:"+hello.toString());
}
}
首先访问ServletContextOne,然后再去访问ServletContextTwo,就可以获取得到在ServletContextOne中存入的数据即可。
5.2、获取得到文件mime类型和全局初始化参数
1、在web.xml全局配置文件中来进行添加对应的全局初始化参数
<context-param>
<param-name>companyname</param-name>
<param-value>lig</param-value>
</context-param>
2、编写一个servlet来统一进行验证:
@WebServlet(name = "ServletContextThree", value = "/ServletContextThree")
public class ServletContextThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String file1 = "a.txt";
String file2 = "a.mp3";
String mimeType1 = servletContext.getMimeType(file1);
String mimeType2 = servletContext.getMimeType(file2);
System.out.println(file1+"的资源类型是:"+mimeType1);
System.out.println(file2+"的资源类型是:"+mimeType2);
System.out.println("------------------------------");
System.out.println("------------------------------");
System.out.println("------------------------------");
String companyname = servletContext.getInitParameter("companyname");
System.out.println("全局初始化参数是:"+companyname);
}
}
对应的控制台显示:
a.txt的资源类型是:text/plain
a.mp3的资源类型是:audio/mpeg
------------------------------
------------------------------
------------------------------
全局初始化参数是:lig
5.3、获取得到web资源路径
-
- String getRealPath(String path);根据资源名称得到资源的绝对路径.
- getResourceAsStream(String path) ;返回制定路径文件的流
注意: filepath:直接从项目的根目录开始写
对应的代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获得文件的绝对路径 getRealPath()这个方法的路径已经在项目下了, 已经到了web目录下了
String realPath = getServletContext().getRealPath("a.txt");
System.out.println("realPath="+realPath);
//2.获得文件的输入流 getResourceAsStream(String path);这个方法的路径已经在项目下了, 已经到了web目录下了
//new FileInputStream(realPath);
InputStream is = getServletContext().getResourceAsStream("a.txt");
System.out.println(is);
}
控制台输出结果:
realPath=D:\hello\myproject\javaweb\out\artifacts\servlet_one_war_exploded\a.txt
java.io.ByteArrayInputStream@44554de5
5.4、ServletContext小练习
需求:统计网站一共被访问了多少次?
首先写两个servlet来进行功能区分:一个负责统计,另外一个负责显示
书写两个servlet:
@WebServlet(name = "LoginCountServlet", value = "/LoginCountServlet")
public class LoginCountServlet extends HttpServlet {
private static AtomicInteger count = new AtomicInteger(1);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
/**
* 这里应该是通过一个唯一标识来表示登录的次数,过滤掉重复登录的情况。这里是简单,统计所有的情况即可
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
Object loginCount = servletContext.getAttribute("loginCount");
if (Objects.isNull(loginCount)) {
servletContext.setAttribute("loginCount", count);
} else {
count.getAndDecrement();
servletContext.setAttribute("loginCount", count);
}
}
}
负责展示的servlet:
@WebServlet(name = "ShowLoginServlet", value = "/ShowLoginServlet")
public class ShowLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
AtomicInteger loginCount = (AtomicInteger) servletContext.getAttribute("loginCount");
int currentCount = loginCount.get();
response.getWriter().println("您是第"+currentCount+"位登录用户");
}
}
分别请求并进行展示即可
6、request
在使用javaweb进行开发过程中,每个请求都会被Tomcat服务器封装称为一个request请求对象,request请求对象封装了一个请求中的所有信息。
request代表请求对象. 原型是HttpServletRequest, 服务器创建好的, 以形参的方式存在doGet()/doPost()方法里面
request作用
- 操作请求三部分(行,头,体)
- 请求转发 . 转发这个请求给别人
- 作为域对象存数据 像ServletContext一样,存|取数据
6.1、操作请求行
实际上的操作,就只有获取。因为已经被Tomcat服务器默认给封装好了,所以不需要来对其进行修改。
获取客户机信息(操作请求行)
请求方式 请求路径(URI) 协议版本
GET /request/web/register.htm?username=zs&password=123456 HTTP/1.1
- getMethod();获取请求方式
- getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
- getContextPath();获得当前应用工程名(部署的路径);
- getRequestURI();获得请求地址,不带主机名
- getRequestURL();获得请求地址,带主机名
- getServerPort();获得服务端的端口
- getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
//method=GET
String method = req.getMethod();
System.out.println("method=" + method);
//uri=/request/request01
String uri = req.getRequestURI();
System.out.println("uri=" + uri);
//url=http://localhost:8080/request/request01
StringBuffer url = req.getRequestURL();
System.out.println("url=" + url);
//protocol=HTTP/1.1
String protocol = req.getProtocol();
System.out.println("protocol=" + protocol);
6.2、操作请求头
请求头: 浏览器告诉服务器自己的属性,配置的, 以key value存在, 可能一个key对应多个value。
getHeader(String name)
- User-Agent: 浏览器信息
- Referer:来自哪个网站(防盗链)
//2. 可以获取请求头
String ua = req.getHeader("User-Agent");
System.out.println("ua=" + ua);
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()){
String header = headerNames.nextElement();
String value = req.getHeader(header);
System.out.println(header + "=" + value);
}
6.3、操作请求体
操作请求体:最主要的是用来获取得到请求体中的参数来进行操作的。
对于get请求方式来说,是没有请求体的。所以这里就只针对的是post请求方式。
获得请求参数
法名 | 描述 |
---|---|
String getParameter(String name) | 获得指定参数名对应的值。如果没有则返回null,如果有多个获得第一个。 例如:username=jack |
String[] getParameterValues(String name) | 获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码 |
Map<String,String[]> getParameterMap() | 获得所有的请求参数。key为参数名,value为key对应的所有的值。 |
响应的代码展示:
@WebServlet(name = "Body1Servlet", value = "/Body1Servlet")
public class Body1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取得到单个参数的值,如果没有返回null
String username = request.getParameter("username");
System.out.println("获取得到的参数是:"+username);
System.out.println("----------------------");
// 获取得到指定参数的值,一个key对应的可以有多个value
String[] hobbies = request.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println("hobby对应的爱好有:"+hobby);
}
System.out.println("----------------------");
// 获取得到所有的!
Map<String, String[]> parameterMap = request.getParameterMap();
}
}
浏览器请求:
http://localhost:8080/servlet_one_war_exploded/Body1Servlet?username=lig&hobby=1&hobby=2&hobby=3
对应的控制台显示:
获取得到的参数是:lig
----------------------
hobby对应的爱好有:1
hobby对应的爱好有:2
hobby对应的爱好有:3
----------------------
但是如果参数过多,还要封装到javabean中去,那么调整好对应的参数之后,还需要调用对象之后来进行setXxx方法来进行操作。
非常麻烦,那么直接利用BeanUtils(Apache Commons组件),来简化JavaBean封装数据。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* //1. 获取数据
String username = req.getParameter("username");
String password = req.getParameter("password");
String address = req.getParameter("address");
String phone = req.getParameter("phone");
//2. 封装对象
User user = new User(username , password , address , phone);
System.out.println("user=" + user);
*/
try {
//1. 获取参数 还有一些特殊的地方,爱好,,有三个数据,???
Map<String, String[]> map = req.getParameterMap();
//2. 创建对象
User u = new User();
//3. 把参数封装到对象身上
BeanUtils.populate(u , map);
//4. 打印一下
System.out.println("u=" + u);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
注意:JavaBean属性需要和Map的key一致,也就是说JavaBean属性需要和表单的name一致。
乱码处理
我们在输入一些中文数据提交给服务器的时候,服务器解析显示出来的一堆无意义的字符,就是乱码。
那么这个乱码是如何出现的呢?这是因为浏览器的编码格式和服务器的编码格式是不同的,只有把服务端和客户端的编码格式进行统一之后,那么才不会乱码。
- get方式, 我们现在使用的tomcat>=8.0了, 乱码tomcat已经处理好了
- post方式, 就需要自己处理
在接收参数之前,来对参数进行处理
request.setCharacterEncoding("UTF-8");
请求转发
请求转发的本质就是: 跳转 ,不能跳转到外部的资源,只能跳转到项目内部的资源
但是对于项目内部来说,仍然是一个请求,也就是请求还未处理完成。
request.getRequestDispatcher(url).forward(request, response); //转发
特点
- 请求的路径不会变化
- 一次请求
- 转发可以转发到WEB-INF里面的资源 (WEB-INF的资源,不能直接访问,可以通过servlet进行跳转访问)
来写一个例子:
@WebServlet(name = "ForwardServlet", value = "/ForwardServlet")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("处理完成请求之后");
request.getRequestDispatcher("/WEB-INF/hello.html").forward(request,response);
}
}
注意:对于WEB-INF下面的资源,客户端是无法直接来进行访问的,只能够通过转发的方式来进行请求访问到。
域对象存取数据
ServletContext: 可以存|取值,范围是整个应用程序, AServlet 存值, BServlet能取值
request范围: 一次请求内有效!!!
域对象是一个容器,这种容器主要用于Servlet与Servlet/JSP之间的数据传输使用的。
- Object getAttribute(String name) ;
- void setAttribute(String name,Object object) ;
- void removeAttribute(String name) ;
写一个案例来进行说明:
存数据的servlet:
@WebServlet(name = "StorageServlet", value = "/StorageServlet")
public class StorageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("store","hello,world");
request.getRequestDispatcher("/TakeOutServlet").forward(request,response);
}
}
取数据的servlet:
@WebServlet(name = "TakeOutServlet", value = "/TakeOutServlet")
public class TakeOutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String storeMsg = (String) request.getAttribute("store");
System.out.println("存储的数据是:"+storeMsg);
response.getWriter().println(storeMsg);
}
}
发送对应的请求之后,然后浏览器上会显示:
hello,world
7、response
在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法
7.1、操作响应行
对应的格式:
HTTP/1.1 200
-
设置的API: response.setStatus(int code);
-
一般不需要设置, 可能302 重定向需要设置
-
常见的响应状态码
- 200 成功
- 302 重定向
- 304 读缓存
- 404 客户端错误
- 500 服务器错误
7.2、操作响应头
响应头: 是服务器指示浏览器去做什么
关注的方法: setHeader(String name,String value);
常用的响应头
Refresh:定时跳转 (eg:服务器告诉浏览器5s之后跳转到百度)
Location:重定向地址(eg: 服务器告诉浏览器跳转到xxx)
Content-Disposition: 告诉浏览器下载
Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)
对应的demo:
@WebServlet(name = "RefreshServlet", value = "/RefreshServlet")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Refresh" , " 5; url=http://www.baidu.com");
}
}
告知浏览器,在5秒钟后跳转到百度页面上。
@WebServlet(name = "LocationServlet", value = "/LocationServlet")
public class LocationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location","/RefreshServlet");
// 还有一种更加简单的写法,直接省略上面两个步骤
// resp.sendRedirect("http://www.baicu.com");
// resp.sendRedirect("/RefreshServlet");
}
}
告知浏览器重定向到RefreshServlet这个路径下来,重新发起对应的请求。
转发和重定向区别
- 重定向可以跳转到外面的资源(网站) , 请求转发跳转不了(只能跳转项目内部资源,因为这是由tomcat来完成)
- 请求转发只有一次请求,重定向有两次请求。
- 请求转发的地址栏不会改变,但是重定向的地址栏会发生改变。
- 请求转发和重定向都能跳转到servlet。
- AServlet (request)存值 ----请求转发---到BServlet , 在BServlet里面能取值。
- AServlet (request)存值 ----重定向---到BServlet , 在BServlet里面不能取值!!。
- 因为WEB-INF 文件夹是受保护的,只能由项目内部跳转,所以请求转发可以跳转到里面的资源,但是重定向不行(因为重定向是从浏览器开始)
7.3、操作响应体
响应体影响着浏览器的显示。
响应乱码处理
解决字符流输出中文乱码问题
response.setContentType("text/html;charset=utf-8");
响应的内容类型是文本类型并以utf-8的形式将内容进行响应。
使用字节输出流输出中文乱码问题
//设置浏览器打开方式
response.setHeader("Content-type", "text/html;charset=utf-8");
//得到字节输出流
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write("你好".getBytes("utf-8"));// 使用平台的默认字符(utf-8)集将此 String 编码为 byte 序列
注意:页面输出只能使用其中的一个流实现,两个流是互斥的.
-
为什么会出现乱码?
- iso8859-1不支持中文
- 编码和解码不一致
-
乱码解决
-
请求乱码
- get方式, tomcat>=8.0 不需要解决, 服务器已经解决好了
- post方式
request.setCharacterEncoding("utf-8");
-
响应乱码
response.setContentType("text/html;charset=utf-8");
-
文件下载
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取要下载的文件名称,通过参数传递进来的
String fileName = req.getParameter("file"); //a.jpg
//2. 根据文件找到得到该文件的输入流
// FileInputStream fis = new FileInputStream();
InputStream fis = getServletContext().getResourceAsStream("download/"+fileName);
if(fis == null){
System.out.println("文件路径错误!!!");
}
//设置一下文件的类型,以及告诉浏览器要下载。
String mimeType = getServletContext().getMimeType(fileName);
resp.setContentType(mimeType);
resp.setHeader("Content-Disposition" , "attachment;filename="+fileName);
//3. 使用字节流的方式写给浏览器
OutputStream os = resp.getOutputStream();
byte [] buffer = new byte[1024];
int len = 0 ;
while( (len = fis.read(buffer)) != -1 ){
os.write(buffer , 0 , len);
}
os.close();
fis.close();
}
}
8、request和response总结
对于来自客户端的请求来说,服务器端无所不应。
所以Tomcat服务器会将请求封装成为一个对象,将响应也封装成为一个对象。
对于一个http的请求来说,格式分为:请求行、请求头、请求体。这里都是获取的功能;
对于一个http的响应来说,格式分为:响应行、响应头、响应体。而响应我们可以通过response响应对象来进行响应的设置操作。
对于请求来说,我们可以知道从哪里请求来的,能够接收到什么
对于响应来说,服务器则是告知客户端需要来做些什么