目标
- 怎么处理请求与响应
- web容器到底是怎么实现请求与响应的
- HttpServletRequest处理请求
- http请求头信息有哪些
- get/post获取参数
- get/post编码
- 上传图片怎么解决?(http协议body数据怎么获取?)
- 容器内部如何共享数据?
- 两个servlet程序之间如何调用?
- 内部转发到底是什么逻辑?
- HttpServletResponse处理响应
- http响应头信息有哪些
- 输出内容也是需要编码的!
- 二进制响应
- 重定向
- 直接输出错误状态(404)
1. web容器到底是怎么实现请求与响应的?
- web容器接收到HTTP服务器转发的请求内容
- 初始化HttpServletRequest对象,然后用相关参数进行赋值
- 初始化HttpServletResponse对象
- 初始化HttpServlet的实现类,调用它的service函数,并把HttpServletRequest和HttpServletResponse两个对象传递过去
- service调用doXXX函数,在该函数中,使用request的getParamter()函数获取参数
- 处理相应的业务逻辑
- 设置response的setContentType输出文件类型和编码
- 调用HttpServletResponse的getWriter()函数获得输出用的PrintWriter对象,然后通过函数println()打印输出html
需注意的是:
- 一次请求和响应是一个生命周期,在该周期呢,HttpServletRequest和HttpServletResponse由容器负责创建和销毁,它们不能跨越生命周期
- web容器在一个生命周期内处理的流程已经很清晰了,试想,如果没有web容器,这些工作都要由程序员每次编码实现,很复杂!因此容器将系统自身业务逻辑和软件需求业务逻辑进行分离,开发人员只需要编写软件需求业务逻辑即可!
servlet原来设计是处理各种网络协议的,因此提供的请求和响应对象类型是ServletRequest和ServletResponse,HttpServlet将这两个对象转化为HttpServletRequest和HttpServletResponse,请看具体代码:
然后在service里面调用实际的doXXX相关函数:
2. HttpServletRequest处理请求
-
http请求头信息有哪些?
标示字段 | 含义 |
---|---|
Accept | 客户端支持的数据类型 |
Accept-Charset | 客户端支持的编码 |
Acctpt-Encoding | 客户端支持哪种数据压缩格式 |
Acctpt-Language | 客户端采用的是那种开发语言 |
Host | 客户端想访问的服务器主机 |
if-Modified-Since | 数据在客户端缓存的时间 |
Referer | 客户端从哪个页面来的 |
User-Agent | 客户端的操作系统和浏览器信息 |
Cookie | cookie数据 |
Connection | 持否需要持久链接 |
Content_length | 表示请求消息正文的长度 |
这是ljlj的请求头信息截图:
-
GET/POST获取参数及标头
取得web应用程序环境路径:getContextPath()
获取参数
函数 | 功能描述 |
---|---|
getParameter(参数名称) | 获取指定参数名称的值 |
getParameterValues() | 同一个请求参数会有多个值,返回的是一个数组,返回类型为Enumeration对象 |
getParameterNames() | 返回所有参数的名称 |
getParameterMap | 以key/value的方式返回参数与值 |
获取标头
函数 | 功能描述 |
---|---|
getHeader(参数名称) | 获取指定参数名称的标头 |
getHeaders() | 类似于getParameterValues函数,返回的也是Enumeration对象 |
getHeaderNames() | 返回所有标头参数名称 |
-
GET/POST编码
编码问题主要是针对非ASCII码字符
-------------------------------post请求-------------------------------
浏览器端
Content-Type标头中会设置字符编码
Context-Type:text/html;charset=UTF-8
相当于是做了如下操作:
String text = java.net.URLEncoding.encode("林","UTF-8")
容器端
容器端默认的编码是:ISO-8859-1,所以如果要取得正确的中文字符,就需要:
req.setCharacterEncoding("UTF-8");
-------------------------------get请求-------------------------------
get请求的浏览器端和post一样,不再赘述!
HTTP服务器端
这里为什么和post不一样,post是容器端,而这里是HTTP服务器端?
因为处理URL的是http服务器,而不是web容器,因此get参数不能使用HttpServletRequest对象!!
get获取参数只能是获得参数后,在进行编码转换,它会使用到String对象的getBytes()函数,具体方式如下:
String name = req.getParameter("name");
String name = new String(name.getBytes("ISO-8859-1","UTF-8"));
-
上传图片怎么解决?(http协议body数据怎么获取?)
先来确认一下http协议body数据是什么?http协议一般是由请求行、请求头部、空行和请求数据四部分组成的:
GET请求没有body
GET /hello/index.html HTTP/1.1
#【↑】request line
#【↓】request headers
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Host: localhost:8000
#发送完关闭连接 or 等待
Connection: Keep-Alive
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6
post请求的表单数据就是body
#url的部分仍然是urlencoded
POST /hello/checkUser.html?opt=xxx HTTP/1.1
Referer: http://localhost:8000/hello/index.html
Accept: */*
Accept-Language: zh-cn
#纯文本的编码:不编码
Content-Type: text/plain
Accept-Encoding: gzip, deflate
Host: localhost:8000
Content-Length: 20
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6
#【↑】/r/n
#【↓】request body
hl=zh-CN&source=hp&q=domety
搞清楚body的原理后,把取body数据分为两种,一种是简单的字符串表单,里面的都是文字,另一种就是文件上传了。
---------字符串------------
HttpServletRequest定义了getReader()方法,可以获得BufferReadere对象,通过该对象,就可以访问body中的数据
---------文件(图片)上传------------
有两种方式,一种是用HttpServletRequest的getInputStream()函数获取ServletInputStream对象,是一种串流对象,来处理上传文件。另一种就是servlet3.0中使用的Part进行操作。前一种有点落伍,就不赘述了!
使用Part处理上传图片,必须加上@WebServlet("/upload.do")
-
容器内部如何共享数据?
req.setAttrbute()
req.getAttrbute()
req.getAttrbuteNames()
req.removeAttrbute()
在一个请求的生命周期内,php共享数据靠session,java可以设置HttpServletRequest的属性,来在整个生命周期内,不论转发了多少个servlet,这些属性都存在。
有些保留的属性名称就不要设置了:
-
两个servlet程序之间如何调用?
request.getRequestDispatcher("some.do").include(request,response)
request.getRequestDispatcher("some.do?data=xxx").include(request,response)
-
内部转发到底是什么逻辑?
在调用forward函数前,当前servlet不能对请求进行响应(例如打印字符串),如果有响应但未确认(缓冲区未满或未调用任何清除方法),则响应设置会被忽略。如果有效应且已确认,然后在调用forward()方法,则会抛出IllegalStateException异常
request.getRequestDispatcher("some.do").forward(request.response);
3. HttpServletResponse处理响应
-
http响应头信息有哪些?
头标示 | 头标示解释 |
---|---|
Location | 表示客户端应该到哪里去提取文档 |
Content-Encoding | 文档的编码方式 |
Content-Length | 表示内容长度 |
Content-Type | 表示后面的文档属于哪种MIME类型 |
Last-Modified | 文档的最后修改时间 |
Expires | 文档什么时候过期 |
Refresh | 表示浏览器因该在多少时间之后刷新文档 |
Server | 服务器名字 |
Set-Cookie | 设置和页面关联的cookie |
---------------常规的设置标头的函数---------------
- setHeader() 设置标头名称和值
- addHeader() 给同一标头名称上附加值
- setIntHeader() 如果标头的值是整数
- addIntHeader() 同理
- setDateHeader() 如果标头的值是日期时间
- addDateHeader() 同理
所有的标头设置必须在响应输出确认之前,如果在之后,所有的设置会被忽略
---------------响应输出有缓冲区---------------
只有在缓冲区满或者强制刷新缓冲区的情况下,才会真正的输出内容到客户端浏览器。
- getBufferSize()
- setBufferSize()
- isCommited() 查看缓冲区的内容是否已经确认
- reset()
- resetBuffer()
- flushBuffer()
在调用HttpServletResponse的getWrite()或getOutputStream()方法之后调用setBufferSize(),会抛出IllegalStateException。(也就是在调用输出函数之前就得设置好缓冲区)
在响应输出确认之后,调用reset(),resetBuffer()会抛出IllegalStateException异常。(确认的输出不能随意重置)
-
输出内容也是需要编码的!
HttpServletResponse对象的setCharacterEncoding()和setContextType()都能设置编码,建议使用后者,连同文件内容类型一起设置
response.setContextType("text/html","UTF-8")
-
二进制响应
在大部分情况下,会从HttpServletResponse取得PrintWriter实例,使用println()对浏览器进行字符输出。但是有时候,需要直接对浏览器进行字节输出,这时候可以使用HttpServletResponse的getOutputStream()方法取得ServletOutputStream实例,他是OutputStream的子类。
区分字符流输出和字节流输出,前者主要用来处理文字,包括多国语言的识别。而字节流主要用来处理文字意外的数据,例如二进制文件,图片,PDF文件
这段代码就展示了如果向浏览器输出一个PDF文件,如果浏览器有第三方插件可以在线打开这个字节流pdf文件,那就直接显示,否则提示下载。
-
重定向
response.sendRedirect("http://xxxxxx.com");
重定向其实也是利用了HTTP状态吗和标头,因此重定向前,不能有响应输出确认,否则会抛出异常
-
直接输出错误状态(404)
response.sendError(HttpServletResponse.SC_NOT_FOUND)