【问题标题】:Spring REST service: retrieving JSON from RequestSpring REST 服务:从请求中检索 JSON
【发布时间】:2023-03-29 00:42:01
【问题描述】:

我正在 Spring 3.1 上构建 REST 服务。我为此使用@EnableWebMVC 注释。由于我的服务将只接受 JSON 请求,我还想将传入的请求转储到 MongoDB 集合中以进行日志记录(以及稍后用于数据转换)。我想访问原始 JSON 请求(我可以在非弹簧实现上使用“@Content HttpServletRequest 请求”作为方法参数)。

我是春季新手。所以,请帮助我实现这一目标。谢谢!

更新:问题尚未完全解决。只有我的 GET 测试有效。 POST 失败。因此未选中接受的答案

问题是,即使我创建了一个 HttpServletRequestWrapper,我也无法在处理和包装请求后转发请求。这是发生了什么:

拦截器:

public class DBLogInterceptor extends HandlerInterceptorAdapter {
    MyRequestWrapper requestWrapper;

    private final static Logger logger = Logger.getLogger(DBLogInterceptor.class);

    @Override
    public boolean preHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler) throws Exception 
    {
        requestWrapper = new MyRequestWrapper(request);
        // Code removed, but it just dumps requestWrapper.getBody() into DB
        return super.preHandle(requestWrapper, response, handler);
    }
}

HTTP POST 服务方式

@RequestMapping(method = RequestMethod.POST, consumes="application/json", produces="application/json", value = "employee")
@ResponseBody
public String updateEntity(@RequestBody Employee emp) {
    // Do some DB Stuff. Anyway, the control flow does not reach this place.
    return "Employee " + emp.getName() + " updated successfully!";
}

现在每当我发送 POST 时都会出现异常:

12:04:53,821 DEBUG DBLogInterceptor:22 - {"name":"Van Damme","dept":"Applied Martial Arts"}
12:04:53,843 DEBUG RequestResponseBodyMethodProcessor:117 - Reading [com.test.webapp.login.domain.Employee] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@154174f9]
12:04:53,850 DEBUG ExceptionHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Stream closed
12:04:53,854 DEBUG ResponseStatusExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Streamclosed
12:04:53,854 DEBUG DefaultHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Streamclosed
12:04:53,859 DEBUG DispatcherServlet:910 - Could not complete request
java.io.IOException: Stream closed
        at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:312)
        at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:200)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.ensureLoaded(ByteSourceBootstrapper.java:507)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.detectEncoding(ByteSourceBootstrapper.java:129)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.constructParser(ByteSourceBootstrapper.java:224)
        at org.codehaus.jackson.JsonFactory._createJsonParser(JsonFactory.java:785)
        at org.codehaus.jackson.JsonFactory.createJsonParser(JsonFactory.java:561)
        at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1914)
        at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:124)
        at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:120)
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:91)
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:71)
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:75)
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:156)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

我希望HttpServletRequestWrapper 负责缓存请求。但它不会以某种方式发生。

【问题讨论】:

  • 这是 Jackson 而不是 Spring 的问题,您可以在没有 Spring 的情况下仍然体验到这一点

标签: json spring rest


【解决方案1】:

使用HttpServletRequest 对象,您可以访问客户端用于发出请求的 URL、使用的方法(GET、POST、PUT 等)、查询字符串和标头。

获取 RequestBody 可能有点棘手,可能需要使用 HttpServletRequestWrapper 对象。由于请求正文只能读取一次,因此您需要扩展包装器以访问它,以便您的目标控制器稍后仍可以访问它以将您的 JSON 反序列化为 POJO 对象。

public class MyRequestWrapper extends HttpServletRequestWrapper {
 private final String body;
 public MyRequestWrapper(HttpServletRequest request) throws IOException {
   super(request);
   StringBuilder stringBuilder = new StringBuilder();
   BufferedReader bufferedReader = null;
   try {
     InputStream inputStream = request.getInputStream();
     if (inputStream != null) {
       bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
       char[] charBuffer = new char[128];
       int bytesRead = -1;
       while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
         stringBuilder.append(charBuffer, 0, bytesRead);
       }
     } else {
       stringBuilder.append("");
     }
   } catch (IOException ex) {
       throw ex;
   } finally {
     if (bufferedReader != null) {
       try {
         bufferedReader.close();
       } catch (IOException ex) {
         throw ex;
       }
     }
   }
   body = stringBuilder.toString();
 }

 @Override
 public ServletInputStream getInputStream() throws IOException {
   final ByteArrayInputStream byteArrayInputStream = new     ByteArrayInputStream(body.getBytes());
   ServletInputStream servletInputStream = new ServletInputStream() {
     public int read() throws IOException {
       return byteArrayInputStream.read();
     }
   };
   return servletInputStream;
 }

 @Override
 public BufferedReader getReader() throws IOException {
   return new BufferedReader(new InputStreamReader(this.getInputStream()));
 }

 public String getBody() {
   return this.body;
 }
}

要在中心位置访问请求,您可以使用过滤器或 Spring 拦截器。这两者都是在将请求委托给控制器之前调用的,并且都可以访问 servlet。

这是actual Logging example using a Spring Interceptor

package com.vaannila.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler. HandlerInterceptorAdapter;

public class LoggerInterceptor extends HandlerInterceptorAdapter {
    static Logger logger = Logger.getLogger(LoggerInterceptor.class);

    static {
        BasicConfigurator.configure();
    }

    @Override

    public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {

        logger.info("Before handling the request");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

        logger.info("After handling the request");
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
        HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        logger.info("After rendering the view");
        super.afterCompletion(request, response, handler, ex);
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="viewResolver" class="org.springframework.web.servlet.view.    InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

    <bean id="handlerMapping" class="org.springframework.web.servlet.handler. BeanNameUrlHandlerMapping" p:interceptors-ref="loggerInterceptor" />

    <bean id="loggerInterceptor" class="com.vaannila.interceptor.LoggerInterceptor" />

    <bean id="userService" class="com.vaannila.service.UserServiceImpl" />

    <bean name="/userRegistration.htm" class="com.vaannila.web.UserController" p:userService-ref="userService" p:formView="userForm" p:successView="userSuccess" />

</beans>

在 LoggerInterceptor 中,您可以使用以下代码来访问请求:

MyRequestWrapper myRequestWrapper = new MyRequestWrapper((HttpServletRequest) request);

String body = myRequestWrapper.getBody();
String clientIP = myRequestWrapper.getRemoteHost();
int clientPort = request.getRemotePort();
String uri = myRequestWrapper.getRequestURI();

System.out.println(body);
System.out.println(clientIP);
System.out.println(clientPort);
System.out.println(uri);

【讨论】:

  • @user1323865 - 刚刚注意到不接受。这不再解决您的问题了吗? ;)
  • 是的,我只用 GET 做过测试(我很傻)。我已经用更多细节更新了这个问题。你能帮忙吗?
  • 您可能需要发布 DBLogInterceptor 类,以及 POST 调用的方法/类,以及对服务器的实际物理 Web 请求(如发出 POST 的 JavaScript 代码)
  • FINALIZING:jmort253 的答案只需要一点改变:用 javax.servlet.Filter 替换 spring 拦截器
  • 我收到流关闭异常,谁能帮忙!
【解决方案2】:

我怀疑HttpServletRequestWrapper 是否可以工作...看看 DispatcherServlet 实现:

            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
            if (interceptors != null) {
                for (int i = 0; i < interceptors.length; i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                    if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                        return;
                    }
                    interceptorIndex = i;
                }
            }

            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

它仍然传递对“processedRequest”的引用,它引用了一个流已被读取的HttpServletRequest请求。

【讨论】:

  • 最后,我必须使用过滤器。 like this
【解决方案3】:

我知道这是一个老问题,但对于那些仍在寻找解决方案的人来说,这对我有用:

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.output.TeeOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpLoggingFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(HttpLoggingFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        try {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;

            Map<String, String> requestMap = this
                    .getTypesafeRequestMap(httpServletRequest);
            BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(
                    httpServletRequest);
            BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(
                    httpServletResponse);

            final StringBuilder logMessage = new StringBuilder(
                    "REST Request - ").append("[HTTP METHOD:")
                    .append(httpServletRequest.getMethod())
                    .append("] [PATH INFO:")
                    .append(httpServletRequest.getPathInfo())
                    .append("] [REQUEST PARAMETERS:").append(requestMap)
                    .append("] [REQUEST BODY:")
                    .append(bufferedRequest.getRequestBody())
                    .append("] [REMOTE ADDRESS:")
                    .append(httpServletRequest.getRemoteAddr()).append("]");

            chain.doFilter(bufferedRequest, bufferedResponse);
            logMessage.append(" [RESPONSE:")
                    .append(bufferedResponse.getContent()).append("]");
            logger.debug(logMessage.toString());
        } catch (Throwable a) {
            logger.error(a.getMessage());
        }
    }

    private Map<String, String> getTypesafeRequestMap(HttpServletRequest request) {
        Map<String, String> typesafeRequestMap = new HashMap<String, String>();
        Enumeration<?> requestParamNames = request.getParameterNames();
        while (requestParamNames.hasMoreElements()) {
            String requestParamName = (String) requestParamNames.nextElement();
            String requestParamValue = request.getParameter(requestParamName);
            typesafeRequestMap.put(requestParamName, requestParamValue);
        }
        return typesafeRequestMap;
    }

    @Override
    public void destroy() {
    }

    private static final class BufferedRequestWrapper extends
            HttpServletRequestWrapper {

        private ByteArrayInputStream bais = null;
        private ByteArrayOutputStream baos = null;
        private BufferedServletInputStream bsis = null;
        private byte[] buffer = null;

        public BufferedRequestWrapper(HttpServletRequest req)
                throws IOException {
            super(req);
            // Read InputStream and store its content in a buffer.
            InputStream is = req.getInputStream();
            this.baos = new ByteArrayOutputStream();
            byte buf[] = new byte[1024];
            int read;
            while ((read = is.read(buf)) > 0) {
                this.baos.write(buf, 0, read);
            }
            this.buffer = this.baos.toByteArray();
        }

        @Override
        public ServletInputStream getInputStream() {
            this.bais = new ByteArrayInputStream(this.buffer);
            this.bsis = new BufferedServletInputStream(this.bais);
            return this.bsis;
        }

        String getRequestBody() throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    this.getInputStream()));
            String line = null;
            StringBuilder inputBuffer = new StringBuilder();
            do {
                line = reader.readLine();
                if (null != line) {
                    inputBuffer.append(line.trim());
                }
            } while (line != null);
            reader.close();
            return inputBuffer.toString().trim();
        }

    }

    private static final class BufferedServletInputStream extends
            ServletInputStream {

        private ByteArrayInputStream bais;

        public BufferedServletInputStream(ByteArrayInputStream bais) {
            this.bais = bais;
        }

        @Override
        public int available() {
            return this.bais.available();
        }

        @Override
        public int read() {
            return this.bais.read();
        }

        @Override
        public int read(byte[] buf, int off, int len) {
            return this.bais.read(buf, off, len);
        }

    }

    public class TeeServletOutputStream extends ServletOutputStream {

        private final TeeOutputStream targetStream;

        public TeeServletOutputStream(OutputStream one, OutputStream two) {
            targetStream = new TeeOutputStream(one, two);
        }

        @Override
        public void write(int arg0) throws IOException {
            this.targetStream.write(arg0);
        }

        public void flush() throws IOException {
            super.flush();
            this.targetStream.flush();
        }

        public void close() throws IOException {
            super.close();
            this.targetStream.close();
        }
    }

    public class BufferedResponseWrapper implements HttpServletResponse {

        HttpServletResponse original;
        TeeServletOutputStream tee;
        ByteArrayOutputStream bos;

        public BufferedResponseWrapper(HttpServletResponse response) {
            original = response;
        }

        public String getContent() {
            return bos.toString();
        }

        public PrintWriter getWriter() throws IOException {
            return original.getWriter();
        }

        public ServletOutputStream getOutputStream() throws IOException {
            if (tee == null) {
                bos = new ByteArrayOutputStream();
                tee = new TeeServletOutputStream(original.getOutputStream(),
                        bos);
            }
            return tee;

        }

        @Override
        public String getCharacterEncoding() {
            return original.getCharacterEncoding();
        }

        @Override
        public String getContentType() {
            return original.getContentType();
        }

        @Override
        public void setCharacterEncoding(String charset) {
            original.setCharacterEncoding(charset);
        }

        @Override
        public void setContentLength(int len) {
            original.setContentLength(len);
        }

        @Override
        public void setContentType(String type) {
            original.setContentType(type);
        }

        @Override
        public void setBufferSize(int size) {
            original.setBufferSize(size);
        }

        @Override
        public int getBufferSize() {
            return original.getBufferSize();
        }

        @Override
        public void flushBuffer() throws IOException {
            tee.flush();
        }

        @Override
        public void resetBuffer() {
            original.resetBuffer();
        }

        @Override
        public boolean isCommitted() {
            return original.isCommitted();
        }

        @Override
        public void reset() {
            original.reset();
        }

        @Override
        public void setLocale(Locale loc) {
            original.setLocale(loc);
        }

        @Override
        public Locale getLocale() {
            return original.getLocale();
        }

        @Override
        public void addCookie(Cookie cookie) {
            original.addCookie(cookie);
        }

        @Override
        public boolean containsHeader(String name) {
            return original.containsHeader(name);
        }

        @Override
        public String encodeURL(String url) {
            return original.encodeURL(url);
        }

        @Override
        public String encodeRedirectURL(String url) {
            return original.encodeRedirectURL(url);
        }

        @SuppressWarnings("deprecation")
        @Override
        public String encodeUrl(String url) {
            return original.encodeUrl(url);
        }

        @SuppressWarnings("deprecation")
        @Override
        public String encodeRedirectUrl(String url) {
            return original.encodeRedirectUrl(url);
        }

        @Override
        public void sendError(int sc, String msg) throws IOException {
            original.sendError(sc, msg);
        }

        @Override
        public void sendError(int sc) throws IOException {
            original.sendError(sc);
        }

        @Override
        public void sendRedirect(String location) throws IOException {
            original.sendRedirect(location);
        }

        @Override
        public void setDateHeader(String name, long date) {
            original.setDateHeader(name, date);
        }

        @Override
        public void addDateHeader(String name, long date) {
            original.addDateHeader(name, date);
        }

        @Override
        public void setHeader(String name, String value) {
            original.setHeader(name, value);
        }

        @Override
        public void addHeader(String name, String value) {
            original.addHeader(name, value);
        }

        @Override
        public void setIntHeader(String name, int value) {
            original.setIntHeader(name, value);
        }

        @Override
        public void addIntHeader(String name, int value) {
            original.addIntHeader(name, value);
        }

        @Override
        public void setStatus(int sc) {
            original.setStatus(sc);
        }

        @SuppressWarnings("deprecation")
        @Override
        public void setStatus(int sc, String sm) {
            original.setStatus(sc, sm);
        }

        @Override
        public String getHeader(String arg0) {
            return original.getHeader(arg0);
        }

        @Override
        public Collection<String> getHeaderNames() {
            return original.getHeaderNames();
        }

        @Override
        public Collection<String> getHeaders(String arg0) {
            return original.getHeaders(arg0);
        }

        @Override
        public int getStatus() {
            return original.getStatus();
        }

    }
}

然后只需在 web.xml 中注册过滤器即可。所有感谢:http://wetfeetblog.com/servlet-filer-to-log-request-and-response-details-and-payload/431(我只是做了一些小修复)。

【讨论】:

    【解决方案4】:

    嘿,你能试试这个吗:

    @RequestMapping(method = RequestMethod.POST, consumes="application/json", produces="application/json", value = "/employee")
    @ResponseBody
    public String updateEntity(@RequestBody Employee emp) {
        // Do some DB Stuff. Anyway, the control flow does not reach this place.
        return "Employee " + emp.getName() + " updated successfully!";
    }
    

    这里:如果您使用“/”证明 URI,它允许执行所有操作。例如使用相同的 URI 值获取更新和删除。

    【讨论】:

      【解决方案5】:

      目前在 spring-mvc repo 中,拦截器在 DispatcherServlet#doDispatch(...) 中被调用:

      https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java

      ...
              if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                  return;
              }
      
              try {
                  // Actually invoke the handler.
                  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
              }
              finally {
                  if (asyncManager.isConcurrentHandlingStarted()) {
                      return;
                  }
              }
      
              applyDefaultViewName(request, mv);
              mappedHandler.applyPostHandle(processedRequest, response, mv);
      ...
      

      我可以定义自己的DispatcherServlet,并覆盖doDispatch(...) 以在getInputStream() 上注入带有ByteArrayInputStreamHttpRequestWrapper

      ...
      
      @Override
          protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
                  throws Exception {
      
              RequestWrapper wrappedRequest = new RequestWrapper(request);
              logger.debug("injecting RequestWrapper: " + wrappedRequest);
      
              super.doDispatch(wrappedRequest, response);
      
          }
      ...
      

      这适用于上述情况吗?

      【讨论】:

        【解决方案6】:

        我制作了一个不依赖于 3rd 方库的 Ouputstream 版本,以便于重用。您可以使用这 2 个包装类轻松获取请求和响应正文。
        但无论如何,我必须使用过滤器而不是拦截器来执行此操作。因为正如@user1323865 提到的,在spring 4 中,processedRequest 用于拦截器和处理程序,因此您不能将这些方法用于拦截器。
        如果您使用的是 Writer 版本,也可以在此链接中找到一些帮助。 Capture and log the response body

        
        public class BufferedRequestWrapper extends HttpServletRequestWrapper
        {
            private static final class BufferedServletInputStream extends ServletInputStream
            {
                private ByteArrayInputStream bais;
        
                public BufferedServletInputStream(ByteArrayInputStream bais)
                {
                    this.bais = bais;
                }
        
                @Override
                public int available()
                {
                    return this.bais.available();
                }
        
                @Override
                public int read()
                {
                    return this.bais.read();
                }
        
                @Override
                public int read(byte[] buf, int off, int len)
                {
                    return this.bais.read(buf, off, len);
                }
        
            }
        
            private byte[] mBodyBuffer;
        
            public BufferedRequestWrapper(HttpServletRequest request) throws IOException
            {
                super(request);
        
                InputStream in = request.getInputStream();
        
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int bytesRead = -1;
                while ((bytesRead = in.read(buffer)) > 0)
                {
                    baos.write(buffer, 0, bytesRead);
                }
                mBodyBuffer = baos.toByteArray();
            }
        
            public String getRequestBody()
            {
                return new String(mBodyBuffer, Charset.forName("UTF-8"));
            }
            @Override
            public BufferedReader getReader() throws IOException
            {
                return new BufferedReader(new InputStreamReader(this.getInputStream()));
            }
        
            @Override
            public ServletInputStream getInputStream()
            {
                ByteArrayInputStream in = new ByteArrayInputStream(mBodyBuffer);
                return new BufferedServletInputStream(in);
            }
        }
        
        
        public class BufferedResponseWrapper extends HttpServletResponseWrapper
        {
            private TeeServletOutputStream mTeeOutputStream;
        
            private static class TeeOutputStream extends OutputStream
            {
                private OutputStream mChainStream;
                private OutputStream mTeeStream;
        
                public TeeOutputStream(OutputStream chainStream, OutputStream teeStream)
                {
                    mChainStream = chainStream;
                    mTeeStream = teeStream;
                }
        
                @Override
                public void write(int b) throws IOException
                {
                    mChainStream.write(b);
                    mTeeStream.write(b);
                    mTeeStream.flush();
                }
        
                @Override
                public void close() throws IOException
                {
                    flush();
                    mChainStream.close();
                    mTeeStream.close();
                }
        
                @Override
                public void flush() throws IOException
                {
                    mChainStream.close();
                }
            }
        
            public class TeeServletOutputStream extends ServletOutputStream
            {
                private final TeeOutputStream targetStream;
        
                public TeeServletOutputStream(OutputStream one, OutputStream two)
                {
                    targetStream = new TeeOutputStream(one, two);
                }
        
                @Override
                public void write(int b) throws IOException
                {
                    this.targetStream.write(b);
                }
        
                @Override
                public void flush() throws IOException
                {
                    super.flush();
                    this.targetStream.flush();
                }
        
                @Override
                public void close() throws IOException
                {
                    super.close();
                    this.targetStream.close();
                }
            }
        
            private ByteArrayOutputStream mByteArrayOutputStream;
        
            public BufferedResponseWrapper(HttpServletResponse response) throws IOException
            {
                super(response);
                mByteArrayOutputStream = new ByteArrayOutputStream();
                mTeeOutputStream = new TeeServletOutputStream(super.getResponse().getOutputStream(), mByteArrayOutputStream);
            }
        
            @Override
            public PrintWriter getWriter() throws IOException
            {
                return super.getResponse().getWriter();
            }
        
            @Override
            public ServletOutputStream getOutputStream() throws IOException
            {
                return mTeeOutputStream;
            }
        
            public String getResponseBody()
            {
                return mByteArrayOutputStream.toString();
            }
        
        }
        

        【讨论】:

          【解决方案7】:

          执行此操作的一种简单方法是将请求正文作为字符串获取,然后解析为 Java 对象。你可以随意使用这个字符串。

          所以在你的例子中:

          @RequestMapping(method = RequestMethod.POST, consumes="application/json", produces="application/json", value = "employee")
          @ResponseBody
          public String updateEntity(@RequestBody String empAsString) {
          
              // Do whatever with the json as String 
              System.out.println(empAsString);
          
              // Transform it into the Java Object you want
              ObjectMapper mapper = new ObjectMapper();
              Employee emp = mapper.readValue(empAsString, Employee.class);
          
              // Do some DB Stuff. Anyway, the control flow does not reach this place.
              return "Employee " + emp.getName() + " updated successfully!";
          }
          

          作为一个注释,如果你需要它作为一个列表,你可以使用:

          List<Employee> eventsList =
                          mapper.readValue(jsonInString, mapper.getTypeFactory().constructCollectionType(List.class, Employee.class));
          

          【讨论】:

          • 我正在经历所有这些,试图在不接触代码的情况下找到一些东西(有效的)。有时,更改 4-5 行代码就是答案。此外,再加上@JsonIgnoreProperties(ignoreUnknown = true),当下一次外部 API 更改发生时,这有点面向未来。
          【解决方案8】:

          你需要按如下方式实现requestWrapper:

          public class DocVerificationRequestWrapper extends HttpServletRequestWrapper {
           private final String body;
           public DocVerificationRequestWrapper(HttpServletRequest request) throws IOException {
             super(request);
             StringBuilder stringBuilder = new StringBuilder();
             BufferedReader bufferedReader = null;
             try {
               InputStream inputStream = request.getInputStream();
               if (inputStream != null) {
                 bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                 char[] charBuffer = new char[128];
                 int bytesRead = -1;
                 while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                   stringBuilder.append(charBuffer, 0, bytesRead);
                 }
               } else {
                 stringBuilder.append("");
               }
             } catch (IOException ex) {
                 throw ex;
             } finally {
               if (bufferedReader != null) {
                 try {
                   bufferedReader.close();
                 } catch (IOException ex) {
                   throw ex;
                 }
               }
             }
             body = stringBuilder.toString();
           }
          
           @Override
           public ServletInputStream getInputStream() throws IOException {
             final ByteArrayInputStream byteArrayInputStream = new     ByteArrayInputStream(body.getBytes());
             ServletInputStream servletInputStream = new ServletInputStream() {
               public int read() throws IOException {
                 return byteArrayInputStream.read();
               }
          
              @Override
              public boolean isFinished() {
                  // TODO Auto-generated method stub
                  return false;
              }
          
              @Override
              public boolean isReady() {
                  // TODO Auto-generated method stub
                  return false;
              }
          
              @Override
              public void setReadListener(ReadListener listener) {
                  // TODO Auto-generated method stub
          
              }
             };
             return servletInputStream;
           }
          
           @Override
           public BufferedReader getReader() throws IOException {
             return new BufferedReader(new InputStreamReader(this.getInputStream()));
           }
          
           public String getBody() {
             return this.body;
           }
          }
          

          然后在filter类的chain.doFilter方法里面传递requestWrapper对象而不是request对象如下:

          @Override
          public void doFilter(ServletRequest arg0, ServletResponse response, FilterChain chain)
                  throws IOException, ServletException {
          
              logger.info("checking token in filter");
              HttpServletRequest request = (HttpServletRequest) arg0;
          
              DocVerificationRequestWrapper myRequestWrapper = new DocVerificationRequestWrapper((HttpServletRequest) request);
          
              String body = myRequestWrapper.getBody();
              logger.info("body = "+body);
              Token token = null;
              try {
                  JSONObject jsonObj = new JSONObject(body);
                  JSONObject tokenObj = (JSONObject) jsonObj.get("token");
                  Gson gson = new Gson();
                  token = gson.fromJson(tokenObj.toString(), Token.class);
          
                  if(null != token) {
                          if(userVerificationService==null){
                          ServletContext servletContext = request.getServletContext();
                          WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
                          userVerificationService = webApplicationContext.getBean(UserVerificationService.class);
                      }
                          String verStatus = userVerificationService.verifyUser(token);
                          logger.info("verStatus = "+verStatus);
                          if(verStatus != null && verStatus.equalsIgnoreCase("success")) {
                              chain.doFilter(myRequestWrapper, response); //here replacing request with requestWrapper 
                          }else
                              logger.error("Invalid token");
                  }else {
                          logger.error("token missing.");
                  }
              } catch (JSONException e) {
                      logger.error("exception in authetication filter " + e);
              }
          }
          

          从而解决IOStream关闭异常。

          【讨论】:

            【解决方案9】:

            要从 Body 获取数据,您可以尝试在 RequestBodyAdviceAdapter 中读取并重新创建 InputStream:

            @ControllerAdvice
            public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
            
                @Override
                public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
                    String body = IOUtils.toString(inputMessage.getBody(), UTF_8.name());
            
                    HttpInputMessage myMessage = new HttpInputMessage(){
                        @Override
                        public InputStream getBody() throws IOException {
                            return new ByteArrayInputStream(body.getBytes());
                        }
                        @Override
                        public HttpHeaders getHeaders() {
                            return inputMessage.getHeaders();
                        }
                    };
            
                    System.out.println("Data from Body: " + body);
            
                    return super.beforeBodyRead(myMessage, parameter, targetType, converterType);
                }
            
                @Override
                public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
                    System.out.println("Data from Body is empty");
                    return super.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
                }
            
                @Override
                public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
                    return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                }
            
                @Override
                public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
                    return true;
                }
            }
            

            【讨论】:

              【解决方案10】:

              你可以简单地使用:

              import org.apache.commons.io.IOUtils;
              import java.nio.charset.Charset;
              
              String requestBody = IOUtils.toString(request.getInputStream(), Charset.forName("UTF-8").toString());
              

              【讨论】:

                【解决方案11】:

                根据我的经验,开发如下: 使用过滤器来包装ServletRequest,然后你可以重复使用获取请求输入流。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-12-14
                  • 2018-07-09
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-06-05
                  • 2022-11-10
                  相关资源
                  最近更新 更多