文章目录
一、Request1、获取请求消息数据1、获取请求行数据2、获取请求头数据3、请求体数据
2、获取数据通用方法1、获取请求参数通用方法2、请求转发3、共享数据4、获取ServletContext
二、HTTP响应协议三、Response1、response重定向2、response相对/绝对路径3、response输出数据4、response验证码
四、ServletContext对象五、文件下载1、中文文件名乱码问题
一、Request
request 与 response原理
我们知道tomcat会在第一次请求url来创建对应路径映射的servlet对象(如果设置servlet启动时自动创建,则请求url不会重复创建servlet对象)同时,每一次请求,tomcat会先创建response和request对象,其中request对象中会封装请求消息数据然后tomcat会将这两个对象传递给servlet实现类中的service方法,并调用我们可以在service方法中,通过response对象来设置响应消息,最后用户通过浏览器可以获取到这些响应数据(这是省略前端的情况下,一般前端工程师会对响应拦截处理)当然tomcat在响应数据前,会先获取response里面的数据,然后再响应给前端
HttpServletRequest
如果你观察Tomcat的源码就会发现,tomcat有一个HttpServletRequest的实现类org.apache.catalina.connector包下的RequestFacade,同时此包下还有一个ResponseFacade是HttpServletResponse接口的实现类
1、获取请求消息数据
1、获取请求行数据
请求行格式
举例:GET /day1/demo1?name=zhangsan HTTP/1.1GET这个位置:请求方法,可以是POST,DELETE,PUT等等,可通过String getMethod()方法获取/day1:虚拟目录,是在Tomcat设置的,可通过String getContextPath()方法获取/demo1:servlet路径,可通过String getServletPath()方法获取?name=zhangsan:请求参数,可通过String getQueryString()方法获取/day1/demo1:这两个合起来是请求URI,可通过String getRequestURI()方法获取StringBuffer getRequestURL():和上面差不多,只不过它获取的路径会带http://ip地址/day1/demo1HTTP/1.1:协议及版本,可通过String getProtocol()方法获取获取客户机ip地址,可通过String getRemoteAddr()方法获取
package com
.yzpnb
.servlet
;
import javax
.servlet
.*
;
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("/dome1")
public class Dome1 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
);
String servletPath
= req
.getServletPath();
System
.out
.println("servlet路径:"+servletPath
);
String queryString
= req
.getQueryString();
System
.out
.println("请求参数:"+queryString
);
String requestURI
= req
.getRequestURI();
StringBuffer requestURL
= req
.getRequestURL();
System
.out
.println("请求路径:"+requestURI
+"========"+requestURL
);
String protocol
= req
.getProtocol();
System
.out
.println("协议及版本:"+protocol
);
String remoteAddr
= req
.getRemoteAddr();
System
.out
.println("客户机ip:"+remoteAddr
);
}
}
2、获取请求头数据
方法
String getHeader(String name):通过请求头名称获取请求头(就是键值对的形式)Enumeration< String > getHeaderNames():获取所有请求头名称,返回值比较特殊,可以当Enumeration类型为迭代器
package com
.yzpnb
.servlet
;
import javax
.servlet
.*
;
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("/dome1")
public class Dome1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
Enumeration
<String> headerNames
= req
.getHeaderNames();
System
.out
.println("所有请求头");
while(headerNames
.hasMoreElements()){
String s
= headerNames
.nextElement();
String header
= req
.getHeader(s
);
System
.out
.println(s
+" "+header
);
}
String userAgent
= req
.getHeader("user-agent");
if(userAgent
.contains("Chrome")){
System
.out
.println("很好,这是我们推荐的Chrome浏览器");
}else{
System
.out
.println("推荐使用谷歌Chrome浏览器");
}
String referer
= req
.getHeader("referer");
System
.out
.println("用户从:"+referer
+"来");
if(referer
!= null
){
if(referer
.contains("dome1")){
System
.out
.println("正常进入链接");
}else{
System
.out
.println("非正常进入");
}
}else{
System
.out
.println("直接输入网址进入");
}
}
}
3、请求体数据
post请求才有请求体
1、先获取输入流对象:BufferReader getReader():获取字符输入流,ServletInputStream getInputStream():获取字节输入流
发送post请求,需要用到表单,或者用某些模拟浏览器,如果你不会,应该先去补习,而不是学这个
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
BufferedReader reader
= req
.getReader();
String line
= null
;
while((line
= reader
.readLine()) != null
){
System
.out
.println(line
);
}
}
2、获取数据通用方法
1、获取请求参数通用方法
String getParameter(String name):根据参数名称获取参数值,比如username=zhangsan&age=14,那么我们通过参数名username可以获取对应值zhangsan
String[] getParameterValues(String name):根据参数名称获取参数值数组,比如hobby=basketball&hobby=game,通过hobby可以获取数组[basketball,game]Enumeration< String > getParameterNames():获取所有请求参数名称Map< String,String[] > getParameterMap():获取所有参数的map集合
<%--
Created by IntelliJ IDEA.
User: dell
Date: 2020/10/4
Time: 16:42
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title
</title>
</head>
<body>
<form action="/dome1" method="post">
<input type="text" name="username" placeholder="请输入用户名"/><br/>
<input type="text" name="password" placeholder="请输入密码"/><br/>
<input type="checkbox" name="hobby" value="game"/>游戏
<input type="checkbox" name="hobby" value="basketball"/>篮球
<br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
package com
.yzpnb
.servlet
;
import javax
.servlet
.*
;
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
.Enumeration
;
import java
.util
.Map
;
import java
.util
.Set
;
@WebServlet("/dome1")
public class Dome1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
this.doPost(req
, resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
String username
= req
.getParameter("username");
System
.out
.println("username="+username
);
System
.out
.println("==============================");
String
[] hobbies
= req
.getParameterValues("hobby");
if(hobbies
!=null
){
System
.out
.print("hobby=[");
for(String hobby
:hobbies
){
System
.out
.print(hobby
+",");
}
System
.out
.println("]");
System
.out
.println("==============================");
}
Enumeration
<String> parameterNames
= req
.getParameterNames();
while(parameterNames
.hasMoreElements()){
String s
= parameterNames
.nextElement();
System
.out
.print(s
+"=");
String parameter
= req
.getParameter(s
);
System
.out
.println(parameter
);
}
System
.out
.println("==============================");
Map
<String
, String
[]> parameterMap
= req
.getParameterMap();
Set
<String> keySet
= parameterMap
.keySet();
for(String key
:keySet
){
String
[] values
= parameterMap
.get(key
);
if(hobbies
!=null
){
System
.out
.print(key
+"=");
for(String value
:values
){
System
.out
.print(value
+" ");
}
System
.out
.println();
}
}
}
}
解决中文乱码
req
.setCharacterEncoding("utf-8");
这时你可能要问,如果用tomcat8以下版本怎么解决中文乱码,放心,就像人们现在都用智能手机,你还会专门回去用小诺基亚么?还会有人专门回去诺基亚研制触屏,上网么?
2、请求转发
请求转发
一种服务器跳转方式,通常我们一个servlet是完不成一项功能的,一般都是几个servlet配合完成,那么如何在servlet1处理完后跳转到servlet2继续处理呢?就需要请求转发1、RequestDispatcher requestDispatcher = req.getRequestDispatcher(String path);:通过request对象获取请求转发器对象RequestDispatcher2、requestDispatcher.forward(ServletRequest servletRequest,ServletResponse servletResponse);:使用请求转发器对象RequestDispatcher的forward方法转发
package com
.yzpnb
.servlet
;
import javax
.servlet
.*
;
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
.Enumeration
;
import java
.util
.Map
;
import java
.util
.Set
;
@WebServlet("/dome1")
public class Dome1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
req
.getRequestDispatcher("/dome2").forward(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
System
.out
.println("我是dome1Post");
req
.getRequestDispatcher("/dome2").forward(req
,resp
);
}
}
package com
.yzpnb
.servlet
;
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("/dome2")
public class Dome2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome2Post");
}
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome2Get");
}
}
需要注意的点(面试可能问)
1、浏览器地址栏并不会发生改变2、只能请求转发到服务器内部资源,而不能转发到外部,比如www.baidu.com3、转发是一次请求,而不是多次,如果细心你会发现我们forward跳转时,都是将request和response对象传入的,也就是说,无论转发多少次,都是同一此请求
3、共享数据
域对象
一个有作用范围的对象,可以在范围内共享数据,不同域对象作用范围不同
request域
范围是一次请求,通常用于在多个转发资源中共享数据,比如servlet1转发到2,2转发到3,而就算转发10000次,也都在一次请求中,这就是request域void setAttribute(String name,Object obj):存储一个数据到request域中,name是键,obj是数据,在同一个域中,可通过name获取objObject getAttribute(String name):通过键获取值void removeAttribute(String name):移除指定键值对
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
System
.out
.println("我是dome1Post");
req
.setAttribute("name","我是字符串");
req
.setAttribute("age",15);
req
.setAttribute("array",new String[] {"1","2","3"});
req
.getRequestDispatcher("/dome2").forward(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome2Post");
String name
= (String
)req
.getAttribute("name");
Integer age
= (Integer
)req
.getAttribute("age");
String
[] array
= (String
[])req
.getAttribute("array");
System
.out
.println(name
);
System
.out
.println(age
);
System
.out
.println(array
);
}
移除就不演示了,直接request.removeAttribute("name")这样的就可以将值移除出域
4、获取ServletContext
ServletContext
一个非常重要的对象,这里只讲怎么获取,后面会专门讲解此对象ServletContext getServletContext():通过Request对象调用方法获取即可
二、HTTP响应协议
响应消息
前面我们学过请求消息,就是客户端发送给服务端的数据,由请求行,请求头,请求空行,请求体组成响应消息:服务端发送给客户端的数据响应消息由,响应行,响应头,响应空行,响应体组成
响应行组成:协议及版本 响应状态码 状态码描述
状态码:服务器告诉客户端浏览器本次请求与响应的状态,一般由3位数字组成,比如404,502,可以百度HTTP状态码来详细了解分类,下面简单介绍一下状态码分类状态码分类(XX代表随机数字):1XX(表示服务端接收消息,接收了一半,没接收完,等了一段时间只能发送状态码1XX),2XX(表示成功),3XX(302代表重定向,比如我们平常登陆完成后出现5秒后跳转页面,这时跳转去的页面就是重定向页面。304代表访问缓存,比如你的头像,第一次访问后服务器反馈给你,下次你又来请求相同的内容,这时就没必要重复响应,服务器会告诉浏览器你本地有,去访问缓存就行了),4XX(表示客户端错误,404表示你请求的资源不存在,一般路径写错就会报这个错,405表示请求方法不对,比如你发Post请求,而后端只有处理Get请求的方法),5XX(表示服务器错误,一般后端代码出错会报这个错)
响应头
Content-Type:表示响应体数据格式以及编码格式Content-disposition:表示以何种格式打开响应体,没有指定就使用默认值in-line,表示在当前页面打开,一般我们需要指定时使用attachment;filename=XXX,表示以附件形式打开响应体,比如下载时指定使用,filename指定文件名
三、Response
设置响应信息和设置请求信息差不多,就不多赘述了
设置响应行、响应头
setStatus(int sc):设置;响应行状态码setHeader(String name,String value):设置响应头
设置响应头
1、获取输出流(字符输出流PrintWriter getWriter(),字节输出流ServletOutputStream getOutputStream()。)2、使用输出流将数据响应给客户端浏览器
1、response重定向
服务器端重定向
就是用户发送请求到一个servlet,服务器告诉用户这个servlet办不了,你去找X某某办1、重定向需要给浏览器设置状态码3022、需要告诉浏览器重定向路径,需设置location
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
this.doPost(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
System
.out
.println("我是dome1Post");
resp
.sendRedirect("dome2");
}
重定向特点(与请求转发完全相反)
地址栏会随着重定向而改变重定向几次就发送几次请求,也就是不能用request域来传递数据了,因为都不是同一次请求重定向可以访问到其它站点,而不局限与本服务器资源
2、response相对/绝对路径
因为这个是初学者非常容易犯的错,所有特地挑出来讲解
绝对地址:http://ip地址/资源路径相对地址:/资源路径以/开头的是相对路径,不以/开头的是绝对路径相对路径会相对于当前路径范围下,寻找指定资源绝对路径会根据路径直接寻找资源建议给客户端使用的重定向,加上虚拟目录,虚拟目录最好不要写死,以后改一下,要改很多地方,推荐使用request.getContextPath()动态获取虚拟目录
3、response输出数据
最大的问题
就是服务器响应与浏览器编码不一样,会乱码那么响应头中Content-Type参数可以设置编码格式通过设置响应头来规定编码
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
this.doPost(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
resp
.setContentType("text/html;charset=utf-8");
System
.out
.println("我是dome1Post");
ServletOutputStream outputStream
= resp
.getOutputStream();
outputStream
.write("alksdjflkasjfl案例可使肌肤鲁大师".getBytes("utf-8"));
}
4、response验证码
简单的验证码例子,可不要把这种验证码放在项目中用
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
this.doPost(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
int width
= 100;
int height
= 50;
BufferedImage bufferedImage
= new BufferedImage(width
, height
, BufferedImage
.TYPE_INT_RGB
);
Graphics graphics
= bufferedImage
.getGraphics();
graphics
.setColor(Color
.PINK
);
graphics
.fillRect(0,0,width
,height
);
graphics
.setColor(Color
.BLUE
);
graphics
.drawRect(0,0,width
-1,height
-1);
String str
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random
= new Random();
for(int i
= 1;i
< 5;i
++){
int index
= random
.nextInt(str
.length());
char ch
= str
.charAt(index
);
graphics
.drawString(ch
+"",width
/5*i
,height
/2);
}
ImageIO
.write(bufferedImage
, "jpg", resp
.getOutputStream());
}
四、ServletContext对象
ServletContext
代表整个web应用,可以和整个程序的容器(比如servlet,Tomcat等等)通信可以获取MIME类型可以作为域对象,共享数据获取文件的真实路径(服务器路径)
获取
1、通过request对象获取:request.getServletContext();2、直接通过HttpServlet获取(因为HttpServlet继承Servlet接口):this.getServletContext();
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
this.doPost(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
ServletContext servletContext1
= req
.getServletContext();
ServletContext servletContext2
= this.getServletContext();
System
.out
.println("两种方式获取的ServletContext是否是同一个对象:"+(servletContext1
== servletContext2
));
String fileName
= "a.jpg";
String MimeType
= servletContext1
.getMimeType(fileName
);
System
.out
.println("文件a.jpg对应的MIME类型为:"+MimeType
);
servletContext1
.setAttribute("name","zhangsan");
String realPath
= servletContext1
.getRealPath("/index.jsp");
System
.out
.println(realPath
);
File file
= new File(realPath
);
}
五、文件下载
步骤
1、设置响应头,告诉浏览器文件打开方式为附件打开,Content-disposition:attachment;filename=XXX,表示以附件形式打开响应体2、用输入流读取文件,转到输出流输出
@Override
protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
System
.out
.println("我是dome1Get");
this.doPost(req
,resp
);
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
ServletContext servletContext
= req
.getServletContext();
String realPath
= servletContext
.getRealPath("/index.jsp");
FileInputStream fileInputStream
= new FileInputStream(realPath
);
String mimeType
= servletContext
.getMimeType("index.jsp");
resp
.setHeader("content-type",mimeType
);
resp
.setHeader("content-disposition","attachment;filename=index.jsp");
ServletOutputStream outputStream
= resp
.getOutputStream();
byte[] bytes
= new byte[1024 * 8];
int len
= 0;
while((len
= fileInputStream
.read(bytes
)) != -1){
outputStream
.write(bytes
,0,len
);
}
fileInputStream
.close();
}
1、中文文件名乱码问题
中文文件名问题,中文文件名无法正常获取显示
1、获取客户端浏览器版本信息2、根据不同版本信息,响应不同的数据(设置filename文件名的编码方式)
package com
.yzpnb
.utils
;
import sun
.misc
.BASE64Encoder
;
import java
.io
.UnsupportedEncodingException
;
import java
.net
.URLEncoder
;
public class DownloadUtils {
public static String
getFileName(String agent
,String filename
) throws UnsupportedEncodingException
{
if(agent
.contains("MSIE")){
filename
= URLEncoder
.encode(filename
,"utf-8");
filename
= filename
.replace("+"," ");
} else if(agent
.contains("Firefox")){
BASE64Encoder base64Encoder
= new BASE64Encoder();
filename
= "=?utf-8?B?"+base64Encoder
.encode(filename
.getBytes("utf-8"))+"?=";
} else{
filename
= URLEncoder
.encode(filename
,"utf-8");
}
return filename
;
}
}
@Override
protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
req
.setCharacterEncoding("utf-8");
ServletContext servletContext
= req
.getServletContext();
String fileName
= "你好.txt";
String realPath
= servletContext
.getRealPath("/"+fileName
);
FileInputStream fileInputStream
= new FileInputStream(realPath
);
String mimeType
= servletContext
.getMimeType(fileName
);
resp
.setHeader("content-type",mimeType
);
String userAgent
= req
.getHeader("user-agent");
fileName
= DownloadUtils
.getFileName(userAgent
, fileName
);
resp
.setHeader("content-disposition","attachment;filename="+fileName
);
ServletOutputStream outputStream
= resp
.getOutputStream();
byte[] bytes
= new byte[1024 * 8];
int len
= 0;
while((len
= fileInputStream
.read(bytes
)) != -1){
outputStream
.write(bytes
,0,len
);
}
fileInputStream
.close();
}