请求响应流程
服务器处理请求的流程:
- 服务器每次收到请求时,都会为这个请求开辟一个新的线程;
- 服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体;
- 服务器还会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应
response:其类型为HttpServletResponse
ServletResponse –> 与协议无关的类型
HttpServletResponse –> 与http协议相关的类型
http协议中响应的内容有哪些东西呢?
200 ok//响应行(状态码及解释)
Content-Type: text/html;charset=utf-8(响应头:键值对)
//空行
<html>
//内容
</html>
- 状态码:200表示成功、302表示重定向、404表示客户端错误(访问的资源不存在)、500表示服务器端错误
sendError(int sc)
:发送错误状态码,例如404、500;sendError(int sc,String value)
:发送错误状态码,还可以带错误信息;setStatus(int sc)
:发送成功的状态码,可以发送302;
- 响应头(键值对):常用的是单值的方法;Content-Type、Refresh、Location等等
setHeader(String name,String value)
:适用于单值的响应头;此方法最常用addHeader(String name,String value)
:适用于多值的响应头(即往同一个name里面添加值);setIntHeader(String name,int value)
:适用于单值的int类型的响应头;addIntHeader(String name,int value)
:适用于多值的int类型的响应头;setDateHeader(String name,long value)
:适用于单值的毫秒类型的响应头;例如设置过期时间expiresaddDateHeader(String name,long value)
:适用于多值的毫秒类型的响应头;- 案例
- 发送302,设置Location头,完成重定向;
- 定时刷新:设置Refresh头;可以理解为定时重定向
- 禁用浏览器缓存:Cache-Control、pragma、expires;
<meta>
标签可以代替响应头:<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
- 响应体:通常是html,也可以是图片
- response的两个流:两个流不能同时使用
- ServletOutputStream,用来想客户端发送字节数据;
- PrintWriter,用来想客户端发送字符数据!需要设置编码!
- 案例:
- 使用PrintWrite发送字符数据;
- 使用ServletOutputStream发送字节数据(图片);
- response的两个流:两个流不能同时使用
- 重定向:
sendRedirect(String location);
方法
案例1:发送404
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.sendError(404,"您访问的资源存在,就不给你看!");
}
}
案例2:重定向302
创建两个Servlet,BServlet用来发送302和Location,这个Location指向CServlet
下面是BServlet:
public class BServlet extends HttpServlet {
/*
演示重定向:
用户请求BServlet,然后BServlet响应302,给出Location头
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
/*
重定向:
1. 设置Location
2. 发送302状态码
*/
System.out.println("BServlet!");
response.setHeader("Location","/day10/CServlet");
response.setStatus(302);
}
}
下面是CServlet:
public class CServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
System.out.println("CServlet!");
}
}
可以Intellij下看到两个Servlet:
并打开浏览器的开发者工具:BServlet被访问时是302状态,并在响应头中给出了Location
CServlet的状态码是200,由BServlet给出的Location访问到
案例3:Refresh
创建两个Servlet,一个用来设置Refresh,一个用来设置跳转
下面是DServlet:
public class DServlet extends HttpServlet {
/*
演示定时刷新
设置Refresh头,它表示定时刷新
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
/*
下面是用来发送响应体!
*/
PrintWriter write = response.getWriter();
write.print("欢迎XXX登陆!5秒后会自动跳转到主页!您看到的一定是乱码!");
/*
设置名为Refresh的响应头
*/
response.setHeader("Refresh","5;URL=/day10/EServlet");
}
}
下面是EServlet:
public class EServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
response.getWriter().print("还是乱码!");
}
}
用浏览器访问DServlet,页面上出现乱码
等待5秒后跳转到EServlet
案例4:禁用浏览器缓存
public class FServlet extends HttpServlet {
/*
禁用浏览器缓存
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
response.getWriter().print("hello world!");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("expires",-1);
response.setHeader("pragma","no-cache");
}
}
request
请求协议中的数据都可以通过request对象来获取!
- 获取常用信息:
- 获取客户端IP,案例:封IP;
String request.getRemoteAddr()
- 请求方式;
request.getMethod();
- 获取客户端IP,案例:封IP;
- 获取请求头:
String getHeader(String name)
:适用于单值头(这个方法很常用);int getIntHeader(String name)
:适用于单值int类型的请求头;long getDataHeader(String name)
:适用于多值请求头;- 案例
- 通过User-Agent识别用户浏览器;包含系统版本,浏览器及其版本(这些都是浏览器告诉服务器的)
- 防盗链:如果请求不是通过本站的超链接发出的,发送错误状态码404;Referer
- 获取请求URL:
String getScheme()
:获取协议;httpString getServerName()
:获取服务器名;localhostString getServerPort()
:获取服务器端口号;8080String getContextPath()
:获取项目名;上下文路径就是项目路径 /my11String getServletPath()
:获取Servlet路径;/AServletString getQueryString()
:获取参数部分,即问号后面的部分;String getRequestURI()
:获取请求URI,等于项目名+Servlet路径;/my11/AServletString getRequestURL()
:获取请求URL,等于不包含参数的整个请求路径;不带参数的URL
- 获取请求参数:请求参数是客户端发给服务器的,包含在请求体中的是post请求,在URL之后的的get请求
String getParameter(String name)
:获取指定名称的请求参数值,适用于单值请求参数;(很常用)String[] getParameterValues()
:获取指定名称的请求参数值,适用于多值请求参数;Enumeration<String> getParameterNames()
:获取所有请求参数名称;Map<String,String[]> getParameterMap()
:获取所有请求参数,其中key为参数名,value为参数值(很常用);- 案例:超链接参数;
案例:表单数据;
- 请求转发和请求包含
RequestDispatcher rd = request.getResquestDispatcher("/MyServlet");
:参数是被转发或包含的Servlet的路径。过程是首先获取被转发的Servlet的路径,这个路径写在了.xml文件中的url-pattern中;然后调用这个方法获取一个调度员对象;调度员对象有对应的方法来把request和response操作。request和response总是成对出现的!
请求转发:rd.forward(request,response)
这个方法经常使用
请求包含:rd.include(request,response)
有时一个请求需要多个Servlet协作才能完成,所需要在一个Servlet跳转到另一个Servlet!
- 一个请求跨多个Servlet,需要使用转发和包含
- 请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头(留头不留体)
- 请求包含:由两个Servlet共同来完成响应体(都留)
- 无论是请求转发还是请求包含,都在一个请求范围内!请使用同一个request和response!
- request域:Servlet中三大域对象从小到大:request、session、application都有如下三个方法!域中是数据随着这个Servlet的消失而消失,所以在重定向中,存在前面中是数据就没有了
void setAttribute(String name,Object value)
Object getAttribute(String name)
voidremoveAttribute(String name)
- 同一请求范围内使用
request.setAttribute()
和request.getAttribute()
来传值!前一个Servlet调用setAttribute()
保存值,之后一个Servlet调用getAttribute()
取出值!
- 同一请求范围内使用
- 请求转发和重定向的区别:
- 请求转发是一个请求一次响应,而重定向是两次请求两次响应;
- 请求转发地址栏不变化,而重定向会显示后一个请求的地址;
- 请求转发只能转发到本项目其他Servlet,而重定向不只能定向到本项目,还能定向到其他项目;
- 请求转发是服务器端行为,只需给出转发的Servlet路径,而重定向需要给出requestURL,既包含项目名;
请求转发或包含的图示:
下面创建两个Servlet来演示一下请求转发:在oneServlet中
public class oneServlet extends HttpServlet {
/*
演示请求转发
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
System.out.println("oneServlet...");
//设置响应头,这个会出现!
response.setHeader("aaa","AAA");
//设置响应体,但是这句并不能出现!
response.getWriter().print("Hello OneServlet!");
//请求转发,等同于调用twoServlet的service方法
request.getRequestDispatcher("/twoServlet").forward(request,response);
}
}
在twoServlet中:
public class twoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws {
//这句会出现
System.out.println("twoServlet...");
//这句响应体才会出现在浏览器页面上
response.getWriter().print("Hello TwoServlet!");
}
}
结果展示:注意观察地址栏并没有变化,说明浏览器不知道请求了第二个Servlet,但是响应内容却是第二个Servlet!但是在打开工具以后可以看到响应头中添加了在oneServlet中设置的内容!
并且在Intellij中可以看到两个Servlet都被启动了
案例1:获取客户端的IP地址、获取请求方式、获取User-Agent
public class AServlet extends HttpServlet {
/*
演示:获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(OS,浏览器信息)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
//获取IP地址
System.out.println("IP: "+request.getRemoteAddr());
//获取请求方式
System.out.println("请求方式: "+request.getMethod());
//获取名User-Agent的请求头
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent: "+ userAgent);
}
}
可以看到如下信息:
把其中的User-Agent拿出来分析一下:User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
;最前面代表是win10的64位操作系统,后面还包含了浏览器的信息。这时候可以加判断语句来输出客户端用的是什么浏览器:
if(request.getHeader("User-Agent").toLowerCase().contains("Chrome")){
System.out.println("你用的是谷歌浏览器!");
}
案例2:通过request来获取url的相关方法
public class BServlet extends HttpServlet {
/*
通过request来获取url的相关方法
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
//获取请求协议
response.getWriter().print(request.getScheme()+ "<br/>");
//获取服务器名称
response.getWriter().print(request.getServerName()+ "<br/>");
//获取服务器端口号
response.getWriter().print(request.getServerPort()+"<br/>");
//获取项目名
response.getWriter().print(request.getContextPath()+"<br/>");
//获取Servlet路径
response.getWriter().print(request.getServletPath()+"<br/>");
//获取参数部分
response.getWriter().print(request.getQueryString()+"<br/>");
//获取URI
response.getWriter().print(request.getRequestURI()+"<br/>");
//获取请求URL
response.getWriter().print(request.getRequestURL()+"<br/>");
}
}
案例3:演示防盗链
Referer这个请求头表示请求的来源
public class CServlet extends HttpServlet {
/*
使用Referer请求头来演示防盗链
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String referer = request.getHeader("Referer");
System.out.println(referer);
if (referer==null||!referer.contains("localhost")){
response.sendRedirect("http://www.baidu.com");
}else {
System.out.println("Hello!");
}
}
}
当直接访问这个Servlet时,就会跳转到百度。只有从自己写的html访问时才会输出“hello”
下面是相应的html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>防盗链</title>
</head>
<body>
<a href="/my11/CServlet">xxx</a>
</body>
</html>
从这个xxx的超链接点击时候:
当直接访问这个网页的时候就会跳转到百度,这就是防盗链的简单原理
案例4:获取请求参数
首先默认的参数可以用doGet获取,在页面上直接点击“点击这里”超链接,html里面设置的默认信息就会被获取。然后以post方式提交表单,可以用doPost获取相应的参数。
下面是html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试请求参数</title>
</head>
<body>
<h1>测试请求参数</h1>
<a href="/my11/DServlet?xxx=XXX&yyy=YYY">点击这里</a>
<hr/>
<form action="/my11/DServlet" method="post">
用户名:<input type="text" name="user"/><br/>
密码:<input type="password" name="password"/><br/>
爱好:<input type="checkbox" name="hobby" value="cf"/>吃饭
<input type="checkbox" name="hobby" value="sj"/>睡觉
<input type="checkbox" name="hobby" value="ldm"/>撸代码
<br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
下面是Servlet:
public class DServlet extends HttpServlet {
/*
演示request获取请求参数
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("GET:"+request.getParameter("xxx"));
System.out.println("GET:"+request.getParameter("yyy"));
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("user");
String password = request.getParameter("password");
//这个方法并不常用,多值
String[] hobby = request.getParameterValues("hobby");
System.out.println(username+","+password+","+ Arrays.toString(hobby));
/*
测试获取所有请求参数的名称
*/
Enumeration<String> names = request.getParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
}
/*
获取所有请求参数,封装到Map中
*/
Map<String,String[]> map = request.getParameterMap();
for (String name:map.keySet()){
String[] values = map.get(name);
System.out.println(name+": "+Arrays.toString(values));
}
}
}
当直接访问这个html并没有提交信息的时候可以看到:
当填写信息并提交以后,post提交方式的信息就会显示出来:
可以看到由Map集合返回的所有请求参数:
本人是菜鸟一枚,当做学习笔记写博客。谢谢各路大咖驻足审阅