java.lang.IllegalStateException:已经为此请求调用了 getInputStream()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17790048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java.lang.IllegalStateException: getInputStream() has already been called for this request
提问by Mario Dennis
I have a Jersey Web-Service that I need to parse some json data that was send along with a request.
我有一个 Jersey 网络服务,我需要解析一些随请求一起发送的 json 数据。
@POST
@Path ("/authenticate")
@Produces (MediaType.APPLICATION_JSON)
public Response authenticate (@Context HttpServletRequest request)
{
try {
StringBuffer json = new StringBuffer ();
BufferedReader reader = request.getReader();
int line;
while ((line = reader.readLine()) != null)
{
json.append(line);
}
System.out.prinln (json);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return Response.ok().entity(json).build();
}//end authenticate method
This service generates the following Exception:
此服务生成以下异常:
java.lang.IllegalStateException: getInputStream() has already been called for this request
java.lang.IllegalStateException: getInputStream() has already been called for this request
I did some research that suggests a getReader
and getInputStream
cannot be called on the same request. Therefore, it seemed like a getInputStream
instance is already called. How is this possible if I haven't made a explicit call to it? To solve this problem, I used the getInputStream
method instead
我做了一些研究,表明 agetReader
和getInputStream
不能在同一个请求中调用。因此,似乎getInputStream
已经调用了一个实例。如果我没有明确调用它,这怎么可能?为了解决这个问题,我改用了getInputStream
方法
try {
ServletInputStream reader = request.getInputStream();
int line;
while ((line = reader.read()) != -1)
{
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return Response.ok().entity().build();
With this approach, how can I use the int of bytes to get the json?
使用这种方法,如何使用 int 字节来获取 json?
回答by Qwerky
Seems like you're missing a @Consumes
annotation. You realise you can just have a method;
似乎您缺少@Consumes
注释。你意识到你可以拥有一个方法;
@POST
@Path ("/authenticate")
@Consumes (MediaType.APPLICATION_JSON)
@Produces (MediaType.APPLICATION_JSON)
public Response authenticate (String entity) {
//entity contains the posted content
}
Without having to read the stream yourself? If you have a bean representing your consumed JSON , then you can just add it as a method param and jersey will automatically parse it for you;
无需自己阅读流?如果您有一个 bean 代表您使用的 JSON ,那么您只需将其添加为方法参数,jersey 就会自动为您解析它;
@POST
@Path ("/authenticate")
@Consumes (MediaType.APPLICATION_JSON)
@Produces (MediaType.APPLICATION_JSON)
public Response authenticate (AuthBean auth) {
//auth bean contains the parsed JSON
}
class AuthBean {
private String username;
private String password;
// getters/setters
}
Example post;
示例帖子;
{
"username" : "[email protected]",
"password" : "super s3cret"
}
回答by vijay
we can read from the HttpServletRequest only once, so we have to be using HttpServletRequestWrapperinside a filter before reading the request and use the wrapper for subsequent calls for multiple times..
我们只能从 HttpServletRequest 读取一次,所以我们必须在读取请求之前在过滤器中使用HttpServletRequestWrapper并多次使用包装器进行后续调用。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import com.dbs.bds.ingestion.util.MultiReadRequestWrapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SessionValidationFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
log.debug("inside filter");
HttpServletRequest httpServletRequest = new MultiReadRequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(httpServletRequest, servletResponse);
} catch (Exception ex) {
log.error("error inside filter:{}", ex.getMessage());
}
}
}
The below is the code for Wrapper which we used in above Filter.
下面是我们在上面的过滤器中使用的 Wrapper 的代码。
import org.apache.commons.io.input.BoundedInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class MultiReadRequestWrapper extends HttpServletRequestWrapper {
private static final Logger LOG = LoggerFactory.getLogger(MultiReadRequestWrapper.class);
// We include a max byte size to protect against malicious requests,
//since this all has to be read into memory
public static final Integer MAX_BYTE_SIZE = 1_048_576; // 1 MB
private StringBuilder body;
public MultiReadRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = new StringBuilder("");
try (
InputStream bounded = new BoundedInputStream(request.getInputStream(), MAX_BYTE_SIZE);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(bounded));){
String line;
while ((line = bufferedReader.readLine()) != null) {
body.append(line);
}
} catch(Exception e) {
LOG.error(e.getMessage());
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.toString().getBytes());
return new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
//do nothing
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
Now expose the above filter as bean
现在将上面的过滤器公开为 bean
@Bean(name = "sessionValidationFilter")
public Filter getSessionValidationFilter() {
return new SessionValidationFilter();
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public FilterRegistrationBean sessionValidationFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(getSessionValidationFilter());
registration.addUrlPatterns("/v1/create/record");
registration.setName("sessionValidationFilter");
return registration;
}