[关闭]
@cxm-2016 2016-12-26T18:13:54.000000Z 字数 4984 阅读 2566

Java Web (三)——Request 和 Response

Web

版本:1
作者:陈小默
声明:禁止商业,禁止转载


Web服务器收到客户端的http请求,会针对每一次请求,分别创建用于代表请求的Request对象、和一个用于代表响应的response对象。

Request

ServletRequest接口是所有Request实现的顶级接口。HttpServletRequest是继承了顶级接口的接口。ServletRequest内容如下

  1. public Object getAttribute(String name);
  2. public void setAttribute(String name, Object o);
  3. public void removeAttribute(String name);
  4. public Enumeration<String> getAttributeNames();

获得和设置请求编码方式

  1. public String getCharacterEncoding();
  2. public void setCharacterEncoding(String env);

获取请求体的信息

  1. public int getContentLength();
  2. public long getContentLengthLong();
  3. public String getContentType();

读取请求体

  1. public ServletInputStream getInputStream()
  2. public BufferedReader getReader()

读取请求参数

  1. public String getParameter(String name);
  2. public Enumeration<String> getParameterNames();
  3. public String[] getParameterValues(String name);
  4. public Map<String, String[]> getParameterMap();

读取请求头

  1. public Locale getLocale();
  2. public Enumeration<Locale> getLocales();

转发请求

  1. public RequestDispatcher getRequestDispatcher(String path);

获取服务器信息

  1. public String getProtocol();
  2. public String getServerName();
  3. public int getServerPort();

获取客户机信息

  1. public String getRemoteAddr();
  2. public String getRemoteHost();
  3. public int getRemotePort();

获取请求参数

浏览器以什么编码打开页面,它就会用什么编码去发送数据。
服务器默认的码表是iOS-8859-1

对于post提交,我们可以使用

  1. req.characterEncoding = "utf-8"

的方式指定,使服务器解码与服务器编码保持一致。

对于Get提交,我们只能使用URLDecoder解码。

利用请求Request域传递参数

作用范围:整个请求链
生命周期:当服务器收到请求,创建出代表请求的Request对象时,生命周期开始。到请求结束,服务器销毁Request对象时,生命周期结束。
用途:在整个请求链范围共享数据(将数据存入Request域发送给JSP显示)。

请求转发与请求包含

1,请求转发时,response缓冲区中的数据会被清空,但不清空响应头。
2,如果在请求转发前通过Response对象发送了(flush)数据给浏览器,那么在请求转发时会抛出异常。
3,当最后一个Servlet响应了请求,其他Servlet的响应无效。(结合第二条可知,如果在请求转发前响应数据会导致异常,在请求转发后响应数据的操作无效)。
4,同一个Servlet对同一个请求转发多次会抛出异常。

请求转发

  1. servletContext.getRequestDispatcher("/hello").forward(req,resp)

请求包含
请求包含会将两个资源的输出进行合并。但是需要注意的是:被包含的Servlet程序不能改变响应消息的状态码和响应头

  1. val writer = resp.writer
  2. writer.write("---------\n")
  3. writer.flush()
  4. servletContext.getRequestDispatcher("/hello").include(req, resp)
  5. writer.write("\n---------")
  6. writer.flush()

三种资源处理方式的区别

请求方式 特点 注意
请求重定向 多次请求多次响应,上一次的响应指导下一次的请求 响应数据之后重定向,重定向无效。重定向之后响应数据,响应无效
请求转发 一次请求一次响应,转发在内部完成 响应数据之前转发,响应无效。响应数据之后转发,抛出异常
请求包含 请求包含能够将多个Servlet的内容合并 被包含的Servlet程序不能改变响应消息的状态码和响应头

Response

ServletResponse是通用的response基本属性方法。

输出数据

通过Response对象,可以将数据输出到客户端。

Response提供了两种流向客户端响应数据。第一种是PrintWriter

  1. val writer = resp.writer
  2. writer.println("hello world")
  3. writer.print(123)
  4. writer.println(true)
  5. writer.write("hello response")
  6. writer.flush()

第二种方式是使用ServletOutputStream

  1. val output = resp.outputStream
  2. output.println("hello world")
  3. output.print(123)
  4. output.println(false)
  5. output.flush()

注意:使用ServletOutputStream输出中文字符串时需要使用toByteArray()变为字节数组,否则其内部实现进行测试,所有非iOS-8859-1编码的字符会抛出异常。

  1. public void print(String s) throws IOException {
  2. if (s == null)
  3. s = "null";
  4. int len = s.length();
  5. for (int i = 0; i < len; i++) {
  6. char c = s.charAt(i);
  7. //
  8. // XXX NOTE: This is clearly incorrect for many strings,
  9. // but is the only consistent approach within the current
  10. // servlet framework. It must suffice until servlet output
  11. // streams properly encode their output.
  12. //
  13. if ((c & 0xff00) != 0) { // high order byte must be zero
  14. String errMsg = lStrings.getString("err.not_iso8859_1");
  15. Object[] errArgs = new Object[1];
  16. errArgs[0] = Character.valueOf(c);
  17. errMsg = MessageFormat.format(errMsg, errArgs);
  18. throw new CharConversionException(errMsg);
  19. }
  20. write(c);
  21. }
  22. }

另外需要注意的是,在一个响应中,我们只能选择使用一种缓冲区。如果我们已经调用过getOutputStream或者getWriter中的任意一个之后,就不能再次调用另一个方法了。

指定编码格式

Response默认以ISO-8859-1输出字符串,所以当我们输出中文信息的时候就会出现乱码。为了解决乱码问题,服务端在发送数据的时候,需要指定编码。

设置响应头

  1. resp.contentType = "text/html;charset=utf-8"

调用此方法可以在响应头中设置

Content-Type:text/html;charset=utf-8

使用这种方式既指定了服务端的编码格式,又指定了客户端的编码格式。

给响应体设置编码

  1. resp.outputStream.write("hello world".toByteArray(Charset.forName("utf-8")))
  1. resp.characterEncoding = "utf-8"

这种方式指定了响应体的编码方式,但是如果你的编码和客户端解码方式不一样,仍然会造成乱码。

实现下载

在Http中,下载相关的请求头是

Content-Disposition:attachment;filename=a.zip

所以我们在实现下载的时候需要添加此请求头

  1. resp.setHeader("Content-Disposition", "attachment;filename=视频.mp4")
  2. val file = File("/Users/hntjz/Documents/One More Chance.mp4")
  3. val out = resp.outputStream
  4. file.forEachBlock(512 * 1024) { bytes, size ->
  5. out.write(bytes, 0, size)
  6. out.flush()
  7. }

对于上面的代码,运行之后我们会发现,文件名在显示的时候是乱码。那么我们就需要对文件名进行编码,这里我们使用的编码方式是,网络编码。写法如下:

  1. resp.contentType = "text/html;charset=utf-8"
  2. val filename = URLEncoder.encode("视频.mp4", "utf-8")
  3. resp.setHeader("Content-Disposition", "attachment;filename=$filename")

定时刷新页面

通过以下方式可以让当前页面定时刷新

  1. class ConfigServlet : HttpServlet() {
  2. var count = 0
  3. override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
  4. resp.setHeader("Refresh", "1;/*任意的URL地址,这里写当前Servlet地址*/")
  5. val writer = resp.writer
  6. writer.print(count++)
  7. writer.flush()
  8. }
  9. }

控制是否缓存

浏览器会根据指令缓存当前页面的数据,对于某些动态资源,我们可能不希望浏览器去缓存它,那么我们可以发送这些响应头去告诉浏览器,不要缓存这个资源:

  1. resp.setIntHeader("Expires",-1)//设置缓存失效时间,设置小于0的值表示不缓存
  2. resp.setHeader("Cache-Control","no-cache")//设置缓存控制为不缓存
  3. resp.setHeader("Pragma","no-cache")//设置缓存控制为不缓存

如果我们需要指定缓存,可以设置缓存的失效时间:

  1. resp.setDateHeader("Expires", System.currentTimeMillis() + 5000)

请求重定向

  1. resp.status = 302
  2. resp.setHeader("Location", "hello")

或者直接使用

  1. resp.sendRedirect("hello")

常用地址的写法

常用的地址写法有绝对路径和虚拟路径:

绝对路径:以/开头,是相对路径直接拼接得到的最终路径。
相对路径:不以/开头,基于当前所在路径得到的路径。

如果当前路径是给客户端使用的,则需要加上web应用名。
如果当前路径是服务器使用的,可以省略web应用名。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注