Servlet-Reader、InputStream

先来看javax.servlet.ServletRequest中getInputStream、getReader以及getParameter的注释说明

 1 /**
 2  * Retrieves the body of the request as binary data using
 3  * a {@link ServletInputStream}.  Either this method or
 4  * {@link #getReader} may be called to read the body, not both.
 5  *
 6  * @return a {@link ServletInputStream} object containing
 7  * the body of the request
 8  *
 9  * @exception IllegalStateException if the {@link #getReader} method
10  * has already been called for this request
11  *
12  * @exception IOException if an input or output exception occurred
13  */
14 public ServletInputStream getInputStream() throws IOException; 
 1 /**
 2  * Retrieves the body of the request as character data using
 3  * a <code>BufferedReader</code>.  The reader translates the character
 4  * data according to the character encoding used on the body.
 5  * Either this method or {@link #getInputStream} may be called to read the
 6  * body, not both.
 7  *
 8  * @return a <code>BufferedReader</code> containing the body of the request
 9  *
10  * @exception UnsupportedEncodingException  if the character set encoding
11  * used is not supported and the text cannot be decoded
12  *
13  * @exception IllegalStateException if {@link #getInputStream} method
14  * has been called on this request
15  *
16  * @exception IOException if an input or output exception occurred
17  *
18  * @see #getInputStream
19  */
20 public BufferedReader getReader() throws IOException;
 1     /**
 2      * Returns the value of a request parameter as a <code>String</code>,
 3      * or <code>null</code> if the parameter does not exist. Request parameters
 4      * are extra information sent with the request.  For HTTP servlets,
 5      * parameters are contained in the query string or posted form data.
 6      *
 7      * <p>You should only use this method when you are sure the
 8      * parameter has only one value. If the parameter might have
 9      * more than one value, use {@link #getParameterValues}.
10      *
11      * <p>If you use this method with a multivalued
12      * parameter, the value returned is equal to the first value
13      * in the array returned by <code>getParameterValues</code>.
14      *
15      * <p>If the parameter data was sent in the request body, such as occurs
16      * with an HTTP POST request, then reading the body directly via {@link
17      * #getInputStream} or {@link #getReader} can interfere
18      * with the execution of this method.
19      *
20      * @param name a <code>String</code> specifying the name of the parameter
21      *
22      * @return a <code>String</code> representing the single value of
23      * the parameter
24      *
25      * @see #getParameterValues
26      */
27     public String getParameter(String name);

通过注释我们可以知道,getInputStream和getReader只能调用其一,如果已经调用了一个,再去调用另一个时就会抛IllegalStateException(同一个可以被多次调用)。当body中存有参数数据时,通过getInputStream、getReader读取数据,getParameter会被影响。

tomcat对于getInputStream和getReader的处理

tomcat通过两个using标识量来实现getInputStream、getReader的互斥访问(并没有线程安全的相关处理),二者只能调用其一,只要不被关闭输入流,就可以多次调用(即便是多次调用,其实返回的也是同一个对象,只说单线程),但一旦关闭就不可以再从输入流中读取数据了(使用的应该是shutdownInputStream)。

关闭输入后,socket处于半关闭状态(使用的应该是shutdownInputStream),此后仍然可以向client写数据。

注意,getInputStream和getReader针对的是body数据,并不会涉及请求行和头信息,也是因为这样,通常我们会将token信息放在header中,在filter处理token后就不会影响到之后的流处理了。

 1 public ServletInputStream getInputStream() throws IOException {
 2     //getReader类似
 3   if (usingReader) {
 4     throw new IllegalStateException
 5         (sm.getString("coyoteRequest.getInputStream.ise"));
 6   }
 7     //getReader类似
 8   usingInputStream = true;
 9   if (inputStream == null) {
10       //getReader是new CoyoteReader(inputBuffer)
11       //getParameter时调用的是getStream,只是比getInputStream少了using的相关处理,
12       //同样是inputStream = new CoyoteInputStream(inputBuffer)
13     inputStream = new CoyoteInputStream(inputBuffer);
14   }
15   return inputStream;
16 }

tomcat对于getParameter的处理

可以看到getParameter只会处理multipart/form-data、application/x-www-form-urlencoded两种Content-Type,后者就是普通的form,可以通过getParameter(String name)得到对应的value。如果是前者(多部),getParameter(String name)是得不到value的,但可以通过getPart(String name)得到Part,再进一步处理。tomcat对于两种Content-Type数据的存储是分开的,multipart/form-data存在了Collection<Part> parts,application/x-www-form-urlencoded存在了ParameterMap<String, String[]> parameterMap,getParameter时虽然会填充parts,但并不会从parts获取元素(当然,调用getPart也会解析parts,但不会解析parameters)。

注意,如果是post请求,getParameter会涉及body数据。在tomcat的实现中,getParameter会先处理url中的查询参数,然后会检查getInputStream或getReader是否被调用,如果被调用过则返回,如果未被调用,则会完全读取body数据(并没有using标记),此后如果再调用getInputStream或getReader处理body,就没有数据可读了(返回-1)。也就是说getParameter、getInputStream和getReader只能有效调用其一。

 1 //如果调用过getInputStream或getReader,则不对再body处理。
 2 //之前已经对url上的查询参数做了处理,getParameter可以无障碍访问url上的查询参数,
 3 //之后的逻辑仅针对body
 4 if (usingInputStream || usingReader) {
 5   success = true;
 6   return;
 7 }
 8
 9 if( !getConnector().isParseBodyMethod(getMethod()) ) {
10   success = true;
11   return;
12 }
13
14 String contentType = getContentType();
15 if (contentType == null) {
16   contentType = "";
17 }
18 int semicolon = contentType.indexOf(‘;‘);
19 if (semicolon >= 0) {
20   contentType = contentType.substring(0, semicolon).trim();
21 } else {
22   contentType = contentType.trim();
23 }
24 //getParameter方法会处理multipart/form-data、application/x-www-form-urlencoded两种Content-Type(都是form),
25 //但getParameter只能获取到application/x-www-form-urlencoded的数据
26 if ("multipart/form-data".equals(contentType)) {
27     //多部会被放到parts容器中
28   parseParts(false);
29   success = true;
30   return;
31 }
32 if (!("application/x-www-form-urlencoded".equals(contentType))) {
33   success = true;
34   return;
35 }
36 //对application/x-www-form-urlencoded数据进一步处理,数据会存放在parameterMap中

如果我们在filter中对body进行了处理(比如将token存在了body中,需要在filter中处理token),那么在之后的流程中就无法再对body处理了,这该怎么办?
springmvc提供了一个辅助类可以解决类似问题,即org.springframework.web.util.ContentCachingRequestWrapper,其大致原理就是将body数据放在缓存中,以便后续访问。

时间: 2024-10-21 00:05:51

Servlet-Reader、InputStream的相关文章

String 、InputStream、Reader 的转换

1.String –> InputStream InputStrem is = new ByteArrayInputStream(str.getBytes());orByteArrayInputStream stream= new ByteArrayInputStream(str.getBytes()); 2.InputStream–>String inputStream input; StringBuffer out = new StringBuffer();     byte[] b =

Java IO操作——字节流(OutputStream、InputStream)和字符流(Writer、Reader)

学习目标 掌握流的概念 掌握字节流与字符流的作用 掌握文件的标准操作步骤 掌握字节与字符操作的区别 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序中需要数据的时候就用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输出都是以流的形式保存的,流中保存的实际上全部是字节文件. 字节流与字符流 在java.io包中操作文件内容的主要有两大类:字节流和字符流,两类都分为输入和输出操作.在字节流中输出数据主要是使用OutputStream完成,输入

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext

前言 接下来的三篇博客我会分别介绍Servlet的以下三个方面: 1.Servlet程序编写 ----- 生命周期 2.ServletAPI Request Response 3.Cookie 和 Session Servlet的作用:Servlet 用来 动态web资源 开发 静态web资源 : 固定数据文件 动态web资源 : 通过程序动态生成数据文件 Servlet技术基于Request-Response编程模型 ---- HTTP协议也是基于请求响应 模型 * Servlet技术 用来

字符流Reader、Writer,字符转换流 OutputStreamWriter、InputStreamReader

Reader.Writer是所有字符流的超类. Writer类方法 void write(String str) void write(char[] cbuf, int off, int len); void flush() void close OutputStreamWriter构造方法字符编码默认为系统编码,可以不写 FileWriter的append默认为false,可以不写.另一个构造(File file, boolean append) Writer out = new FileWr

Servlet - Upload、Download、Async

Upload.Download.Async 标签 : Java与Web Upload-上传 随着3.0版本的发布,文件上传终于成为Servlet规范的一项内置特性,不再依赖于像Commons FileUpload之类组件,因此在服务端进行文件上传编程变得不费吹灰之力. 客户端 要上传文件, 必须利用multipart/form-data设置HTML表单的enctype属性,且method必须为POST: <form action="simple_file_upload_servlet.do

Servlet – Upload、Download、Async、动态注册

Upload-上传随着3.0版本的发布,文件上传终于成为Servlet规范的一项内置特性,不再依赖于像Commons FileUpload之类组件,因此在服务端进行文件上传编程变得不费吹灰之力. 客户端要上传文件, 必须利用multipart/form-data设置HTML表单的enctype属性,且method必须为POST:<form action="simple_file_upload_servlet.do" method="POST" enctype=

1、Servlet 2、ServletConfig 3、ServletContext 4、HttpUrlConnection

1.Servlet 2.ServletConfig 3.ServletContext 4.HttpUrlConnection 07. 五 / J2EE / 没有评论 一.第一个Servlet的编写过程1.建立JavaWeb应用的目录HelloServlet—–应用名称WEB-INF——–classes:Servlet就放在此处libweb.xml 2.在classes目录中建立Servlet类一个Servlet必须直接或间接地实现javax.servlet.Servlet接口一般我们选择继承ja

Unit01: Servlet基础 、 HTTP协议

Unit01: Servlet基础 . HTTP协议 在页面上输出当前时间 package web; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import

Reader与InputStream两个类中的read()的区别

InputStream类的read()方法是从流里面取出一个字节,他的函数原型是 int read(); ,Reader类的read()方法则是从流里面取出一个字符(一个char),他的函数原型也是 int read(); . 我们都知道java使用的是UNICODE字符集,在java中字符和字符串都是使用UTF-16BE编码方式,即一个字符两个字节,在内存中高位在低字节,这也是BE的由来,BIG ENDIAN可以理解成大位的在开头,例如一个char的值是0XAC56,那么在内存中的形式就是AC

【Servlet】(1)Servlet简介、Servlet底层原理、Servlet实现方式、Servlet生命周期

2017年07月26日 00:16:04 YI_DIAN_DIAN 阅读数:5832 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq_35415600/article/details/76100568 一.Servlet简介 1.Servlet定义: Servlet(Server Applet)是Java Servlet的简称,是为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容.