@cxm-2016
2016-12-26T18:13:54.000000Z
字数 4984
阅读 2566
Web
版本:1
作者:陈小默
声明:禁止商业,禁止转载
Web服务器收到客户端的http请求,会针对每一次请求,分别创建用于代表请求的Request对象、和一个用于代表响应的response对象。
ServletRequest
接口是所有Request实现的顶级接口。HttpServletRequest
是继承了顶级接口的接口。ServletRequest
内容如下
域
public Object getAttribute(String name);
public void setAttribute(String name, Object o);
public void removeAttribute(String name);
public Enumeration<String> getAttributeNames();
获得和设置请求编码方式
public String getCharacterEncoding();
public void setCharacterEncoding(String env);
获取请求体的信息
public int getContentLength();
public long getContentLengthLong();
public String getContentType();
读取请求体
public ServletInputStream getInputStream()
public BufferedReader getReader()
读取请求参数
public String getParameter(String name);
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
读取请求头
public Locale getLocale();
public Enumeration<Locale> getLocales();
转发请求
public RequestDispatcher getRequestDispatcher(String path);
获取服务器信息
public String getProtocol();
public String getServerName();
public int getServerPort();
获取客户机信息
public String getRemoteAddr();
public String getRemoteHost();
public int getRemotePort();
浏览器以什么编码打开页面,它就会用什么编码去发送数据。
服务器默认的码表是iOS-8859-1
对于post提交,我们可以使用
req.characterEncoding = "utf-8"
的方式指定,使服务器解码与服务器编码保持一致。
对于Get提交,我们只能使用URLDecoder解码。
作用范围:整个请求链
生命周期:当服务器收到请求,创建出代表请求的Request对象时,生命周期开始。到请求结束,服务器销毁Request对象时,生命周期结束。
用途:在整个请求链范围共享数据(将数据存入Request域发送给JSP显示)。
1,请求转发时,response缓冲区中的数据会被清空,但不清空响应头。
2,如果在请求转发前通过Response对象发送了(flush)数据给浏览器,那么在请求转发时会抛出异常。
3,当最后一个Servlet响应了请求,其他Servlet的响应无效。(结合第二条可知,如果在请求转发前响应数据会导致异常,在请求转发后响应数据的操作无效)。
4,同一个Servlet对同一个请求转发多次会抛出异常。
请求转发
servletContext.getRequestDispatcher("/hello").forward(req,resp)
请求包含
请求包含会将两个资源的输出进行合并。但是需要注意的是:被包含的Servlet程序不能改变响应消息的状态码和响应头
val writer = resp.writer
writer.write("---------\n")
writer.flush()
servletContext.getRequestDispatcher("/hello").include(req, resp)
writer.write("\n---------")
writer.flush()
请求方式 | 特点 | 注意 |
---|---|---|
请求重定向 | 多次请求多次响应,上一次的响应指导下一次的请求 | 响应数据之后重定向,重定向无效。重定向之后响应数据,响应无效 |
请求转发 | 一次请求一次响应,转发在内部完成 | 响应数据之前转发,响应无效。响应数据之后转发,抛出异常 |
请求包含 | 请求包含能够将多个Servlet的内容合并 | 被包含的Servlet程序不能改变响应消息的状态码和响应头 |
ServletResponse
是通用的response基本属性方法。
通过Response对象,可以将数据输出到客户端。
Response提供了两种流向客户端响应数据。第一种是PrintWriter
val writer = resp.writer
writer.println("hello world")
writer.print(123)
writer.println(true)
writer.write("hello response")
writer.flush()
第二种方式是使用ServletOutputStream
val output = resp.outputStream
output.println("hello world")
output.print(123)
output.println(false)
output.flush()
注意:使用ServletOutputStream输出中文字符串时需要使用toByteArray()
变为字节数组,否则其内部实现进行测试,所有非iOS-8859-1
编码的字符会抛出异常。
public void print(String s) throws IOException {
if (s == null)
s = "null";
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
//
// XXX NOTE: This is clearly incorrect for many strings,
// but is the only consistent approach within the current
// servlet framework. It must suffice until servlet output
// streams properly encode their output.
//
if ((c & 0xff00) != 0) { // high order byte must be zero
String errMsg = lStrings.getString("err.not_iso8859_1");
Object[] errArgs = new Object[1];
errArgs[0] = Character.valueOf(c);
errMsg = MessageFormat.format(errMsg, errArgs);
throw new CharConversionException(errMsg);
}
write(c);
}
}
另外需要注意的是,在一个响应中,我们只能选择使用一种缓冲区。如果我们已经调用过getOutputStream
或者getWriter
中的任意一个之后,就不能再次调用另一个方法了。
Response默认以ISO-8859-1
输出字符串,所以当我们输出中文信息的时候就会出现乱码。为了解决乱码问题,服务端在发送数据的时候,需要指定编码。
设置响应头
resp.contentType = "text/html;charset=utf-8"
调用此方法可以在响应头中设置
Content-Type:text/html;charset=utf-8
使用这种方式既指定了服务端的编码格式,又指定了客户端的编码格式。
给响应体设置编码
resp.outputStream.write("hello world".toByteArray(Charset.forName("utf-8")))
resp.characterEncoding = "utf-8"
这种方式指定了响应体的编码方式,但是如果你的编码和客户端解码方式不一样,仍然会造成乱码。
在Http中,下载相关的请求头是
Content-Disposition:attachment;filename=a.zip
所以我们在实现下载的时候需要添加此请求头
resp.setHeader("Content-Disposition", "attachment;filename=视频.mp4")
val file = File("/Users/hntjz/Documents/One More Chance.mp4")
val out = resp.outputStream
file.forEachBlock(512 * 1024) { bytes, size ->
out.write(bytes, 0, size)
out.flush()
}
对于上面的代码,运行之后我们会发现,文件名在显示的时候是乱码。那么我们就需要对文件名进行编码,这里我们使用的编码方式是,网络编码。写法如下:
resp.contentType = "text/html;charset=utf-8"
val filename = URLEncoder.encode("视频.mp4", "utf-8")
resp.setHeader("Content-Disposition", "attachment;filename=$filename")
通过以下方式可以让当前页面定时刷新
class ConfigServlet : HttpServlet() {
var count = 0
override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
resp.setHeader("Refresh", "1;/*任意的URL地址,这里写当前Servlet地址*/")
val writer = resp.writer
writer.print(count++)
writer.flush()
}
}
浏览器会根据指令缓存当前页面的数据,对于某些动态资源,我们可能不希望浏览器去缓存它,那么我们可以发送这些响应头去告诉浏览器,不要缓存这个资源:
resp.setIntHeader("Expires",-1)//设置缓存失效时间,设置小于0的值表示不缓存
resp.setHeader("Cache-Control","no-cache")//设置缓存控制为不缓存
resp.setHeader("Pragma","no-cache")//设置缓存控制为不缓存
如果我们需要指定缓存,可以设置缓存的失效时间:
resp.setDateHeader("Expires", System.currentTimeMillis() + 5000)
resp.status = 302
resp.setHeader("Location", "hello")
或者直接使用
resp.sendRedirect("hello")
常用的地址写法有绝对路径和虚拟路径:
绝对路径:以/
开头,是相对路径直接拼接得到的最终路径。
相对路径:不以/
开头,基于当前所在路径得到的路径。
如果当前路径是给客户端使用的,则需要加上web应用名。
如果当前路径是服务器使用的,可以省略web应用名。