ServletResponse的java.lang.IllegalStateException错误

本文详细解析了在使用Jetty框架时出现的java.lang.IllegalStateException异常,该异常发生在同时调用了ServletResponse接口中的getOutputStream和getWriter方法。文章通过源码分析解释了这两种方法不能混合使用的根本原因。

java.lang.IllegalStateException: STREAM
at org.eclipse.jetty.server.Response.getWriter(Response.java:707)


java.lang.IllegalStateException: WRITER
at org.eclipse.jetty.server.Response.getOutputStream(Response.java:681)

今天在写一个框架的时候遇到一个问题,记录一下,报错如上,查看javax.servlet.ServletResponse接口中的定义如下:

/**
* Returns a {@link ServletOutputStream} suitable for writing binary
* data in the response. The servlet container does not encode the
* binary data.

* <p> Calling flush() on the ServletOutputStream commits the response.

* Either this method or {@link #getWriter} may
* be called to write the body, not both.
*
* @return a {@link ServletOutputStream} for writing binary data
*
* @exception IllegalStateException if the <code>getWriter</code> method
* has been called on this response
*
* @exception IOException if an input or output exception occurred
*
* @see #getWriter
*
*/

public ServletOutputStream getOutputStream() throws IOException;


/**
* Returns a <code>PrintWriter</code> object that
* can send character text to the client.
* The <code>PrintWriter</code> uses the character
* encoding returned by {@link #getCharacterEncoding}.
* If the response's character encoding has not been
* specified as described in <code>getCharacterEncoding</code>
* (i.e., the method just returns the default value
* <code>ISO-8859-1</code>), <code>getWriter</code>
* updates it to <code>ISO-8859-1</code>.
* <p>Calling flush() on the <code>PrintWriter</code>
* commits the response.
* <p>Either this method or {@link #getOutputStream} may be called
* to write the body, not both.
*
*
* @return a <code>PrintWriter</code> object that
* can return character data to the client
*
* @exception UnsupportedEncodingException
* if the character encoding returned
* by <code>getCharacterEncoding</code> cannot be used
*
* @exception IllegalStateException
* if the <code>getOutputStream</code>
* method has already been called for this
* response object
*
* @exception IOException
* if an input or output exception occurred
*
* @see #getOutputStream
* @see #setCharacterEncoding
*
*/

public PrintWriter getWriter() throws IOException;

注意这两句:
Either this method or {@link #getWriter} may be called to write the body, not both.
Either this method or {@link #getOutputStream} may be called to write the body, not both.

再看jetty的源码,jetty.server.Response的源码:

public ServletOutputStream getOutputStream() throws IOException
{
if (_outputState!=NONE && _outputState!=STREAM)
throw new IllegalStateException("WRITER");

ServletOutputStream out = _connection.getOutputStream();
_outputState=STREAM;
return out;
}



public PrintWriter getWriter() throws IOException
{
if (_outputState!=NONE && _outputState!=WRITER)
throw new IllegalStateException("STREAM");

/* if there is no writer yet */
if (_writer==null)
{
/* get encoding from Content-Type header */
String encoding = _characterEncoding;

if (encoding==null)
{
/* implementation of educated defaults */
if(_cachedMimeType != null)
encoding = MimeTypes.getCharsetFromContentType(_cachedMimeType);

if (encoding==null)
encoding = StringUtil.__ISO_8859_1;

setCharacterEncoding(encoding);
}

/* construct Writer using correct encoding */
_writer = _connection.getPrintWriter(encoding);
}
_outputState=WRITER;
return _writer;
}

从源码的第3-4行可以得以验证,也就是说对于同一个response实例你只能使用其中的一个方法,不能两者混用。
### java.lang.IllegalStateException 异常来源及原因 `java.lang.IllegalStateException` 是一种运行时异常,表示在当前对象的状态下不允许调用特定方法或执行某些操作[^1]。这种异常通常发生在以下几种情况下: 1. **响应已提交后尝试调用 `sendError()` 方法**:当服务器响应已经完成(即响应数据已被发送到客户端),此时再调用 `sendError()` 方法会导致此异常。例如,在 Spring MVC 或 Servlet 环境中,如果在过滤器、拦截器或控制器中多次尝试修改响应内容,可能会引发该问题[^1]。 2. **超出最大上传文件大小限制**:在文件上传过程中,如果上传的文件大小超过了配置的最大限制,Tomcat 等容器会抛出 `FileSizeLimitExceededException`,并将其包装为 `IllegalStateException` 异常。这通常是由于未正确配置 `max-file-size` 或 `max-request-size` 参数导致的[^2]。 3. **重复键冲突**:在使用 Java Stream API 时,如果尝试将具有相同键的元素插入到 Map 中,并且没有指定合并策略,系统会抛出 `IllegalStateException` 异常。这是因为在默认情况下,Stream 的 `toMap` 方法不允许存在重复键[^3]。 ### 解决方法 #### 1. 响应已提交问题 确保在 Servlet 或 Spring MVC 中不会重复修改响应内容。可以通过检查代码逻辑,避免在响应已经提交后再次调用 `sendError()` 或其他修改响应的方法。例如: ```java if (!response.isCommitted()) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occurred"); } ``` #### 2. 文件上传大小限制问题 调整 Tomcat 或其他应用服务器的文件上传大小限制。以 Spring Boot 为例,可以在 `application.properties` 中设置如下参数: ```properties spring.servlet.multipart.max-file-size=50MB spring.servlet.multipart.max-request-size=50MB ``` 或者直接在 Tomcat 配置文件中调整相关参数。 #### 3. 重复键冲突问题 在使用 Stream API 的 `toMap` 方法时,可以提供一个合并函数来处理重复键的情况。例如: ```java Map<Integer, String> result = list.stream() .collect(Collectors.toMap( item -> item.getId(), item -> item.getName(), (existing, replacement) -> existing // 如果发生冲突,保留现有值 )); ``` 通过上述方法,可以有效解决 `java.lang.IllegalStateException` 异常的不同场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值