zoukankan      html  css  js  c++  java
  • protobuf接口调用报错:java.nio.charset.MalformedInputException: Input length = 1

      使用protobuf定义的接口api发起http请求报错,日志如下:

    [2018-04-16 17:34:58] DEBUG AbstractPool:107 - server updated, node=10.211.95.79:80, server={ node: 10.211.95.79:80, hostname: 10.211.95.79, port: 80, status: 1, weight: 8, capacity: 128, breaker: { state :CLOSED, working: 0, delay: 15000000000, failureThreshold: [80/100, 0.8], successThreshold: [12/16, 0.75]}, version: 2497984700 }
    [2018-04-16 17:35:16] DEBUG EnvironmentInterceptor:33 - EnvironmentInterceptor.preHandle requesturl:http://127.0.0.1:8080/ms-search-war/ms.search.searchService/getSearchRank
    [2018-04-16 17:35:16] DEBUG EnvironmentInterceptor:34 - RequestHeads,User-Agent=Apache-HttpClient/4.1.1 (java 1.5),X-Identity-ID=15077870000,X-Auth-Token=123,X-Login-Type=2
    [2018-04-16 17:35:16] ERROR ServletRequestParser:46 - IOException: request=/ms-search-war/ms.search.searchService/getSearchRank, ex=java.nio.charset.MalformedInputException: Input length = 1
    [2018-04-16 17:35:16] DEBUG EnvironmentInterceptor:55 - EnvironmentInterceptor completed, requesturl= http://127.0.0.1:8080/ms-search-war/ms.search.searchService/getSearchRank and server delayTime = 141

      我们来看ServletRequestParser的报错代码行:

    import java.io.IOException;
    import java.math.BigDecimal;
    import javax.servlet.http.HttpServletRequest;
    import javax.xml.bind.DatatypeConverter;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.MediaType;
    import com.google.protobuf.ByteString;
    import com.google.protobuf.Descriptors;
    import com.google.protobuf.InvalidProtocolBufferException;
    import com.google.protobuf.Message;
    import com.google.protobuf.util.JsonFormat;
    public class ServletRequestParser {
      private static final Logger logger = LoggerFactory.getLogger(ServletRequestParser.class);
    
      public static Message parse(HttpServletRequest request, Message prototype) {
        String requestMethod = request.getMethod();
        Descriptors.Descriptor inputType = prototype.getDescriptorForType();
        Message.Builder builder = prototype.newBuilderForType();
        if (HttpMethod.POST.matches(requestMethod)) {
          MediaType contentType = ContentType.BINARY;
          try {
            contentType = MediaType.valueOf(request.getContentType());
          } catch (Exception ex) {
          }
          try {
            if (ContentType.isProtobuf(contentType) || ContentType.isBinary(contentType)) {
              return builder.mergeFrom(request.getInputStream()).build();
            } else if (ContentType.isJson(contentType)) {
              request.setCharacterEncoding("utf-8");
              JsonFormat.parser().merge(request.getReader(), builder);
              return builder.build();
            } else {
              logger.error("invalid content-type: {}", contentType);
            }
          } catch (InvalidProtocolBufferException ex) {
            logger.error("InvalidProtocolBuffer: request={}, ex={}", request.getRequestURI(), ex);
          } catch (IOException ex) {
            logger.error("IOException: request={}, ex={}", request.getRequestURI(), ex);
          }
        } else if (HttpMethod.GET.matches(requestMethod)) {
          for (Descriptors.FieldDescriptor field : inputType.getFields()) {
            String[] values = request.getParameterValues(field.getName());
            if (null != values && values.length > 0) {
              if (!field.isRepeated()) {
                Object o = parseFieldValue(field, values[0], builder);
                if (null != o) builder.setField(field, o);
              } else {
                for (String value : values) {
                  Object o = parseFieldValue(field, value, builder);
                  if (null != o) builder.addRepeatedField(field, o);
                }
              }
            }
          }
          return builder.build();
        }
        return null;
      }
    
    

      这里调用了com.google.protobuf.util.JsonFormat的内部类Parser的merge方法进行转换时报错,因为异常只在这里抛出,所以只能怀疑获取Reader对象的这个request.getReader()方法,跟进代码:

    /**
     * Wrapper object for the Coyote request.
     *
     * @author Remy Maucherat
     * @author Craig R. McClanahan
     */
    public class Request implements org.apache.catalina.servlet4preview.http.HttpServletRequest {
    /**
         * Read the Reader wrapping the input stream for this Request.  The
         * default implementation wraps a <code>BufferedReader</code> around the
         * servlet input stream returned by <code>createInputStream()</code>.
         *
         * @return a buffered reader for the request
         * @exception IllegalStateException if <code>getInputStream()</code>
         *  has already been called for this request
         * @exception IOException if an input/output error occurs
         */
        @Override
        public BufferedReader getReader() throws IOException {
    
            if (usingInputStream) {
                throw new IllegalStateException
                    (sm.getString("coyoteRequest.getReader.ise"));
            }
    
            usingReader = true;
            inputBuffer.checkConverter();
            if (reader == null) {
                reader = new CoyoteReader(inputBuffer);
            }
            return reader;
    
        }
    
    

       这里调用的是catalina的Requst对象的getReader方法,返回的是一个BufferedReader对象,这里最终实例化出了一个CoyoteReader对象。

        /**
         * Reader.
         */
        protected CoyoteReader reader = new CoyoteReader(inputBuffer);

      我们来看下CoyoteReader对象:  

    /**
     * Coyote implementation of the buffered reader.
     *
     * @author Remy Maucherat
     */
    public class CoyoteReader
        extends BufferedReader {
    
    
        // -------------------------------------------------------------- Constants
    
    
        private static final char[] LINE_SEP = { '
    ', '
    ' };
        private static final int MAX_LINE_LENGTH = 4096;
    
    
        // ----------------------------------------------------- Instance Variables
    
    
        protected InputBuffer ib;
    
    
        protected char[] lineBuffer = null;
    
    
        // ----------------------------------------------------------- Constructors
    
    
        public CoyoteReader(InputBuffer ib) {
            super(ib, 1);
            this.ib = ib;
        }
    
    }

      再看下它里面的InputBuffer对象:

    /**
     * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
     * OutputBuffer, adapted to handle input instead of output. This allows
     * complete recycling of the facade objects (the ServletInputStream and the
     * BufferedReader).
     *
     * @author Remy Maucherat
     */
    public class InputBuffer extends Reader
        implements ByteChunk.ByteInputChannel, ApplicationBufferHandler {
    
        /**
         * The string manager for this package.
         */
        protected static final StringManager sm = StringManager.getManager(InputBuffer.class);
    
        private static final Log log = LogFactory.getLog(InputBuffer.class);
    
        public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
    
        // The buffer can be used for byte[] and char[] reading
        // ( this is needed to support ServletInputStream and BufferedReader )
        public final int INITIAL_STATE = 0;
        public final int CHAR_STATE = 1;
        public final int BYTE_STATE = 2;
    
    
        /**
         * Encoder cache.
         */
        private static final ConcurrentMap<Charset, SynchronizedStack<B2CConverter>> encoders = new ConcurrentHashMap<>();
    
        // ----------------------------------------------------- Instance Variables
    
        /**
         * The byte buffer.
         */
        private ByteBuffer bb;
    
    
        /**
         * The char buffer.
         */
        private CharBuffer cb;
    
    
        /**
         * State of the output buffer.
         */
        private int state = 0;
    
    
        /**
         * Flag which indicates if the input buffer is closed.
         */
        private boolean closed = false;
    
    
        /**
         * Encoding to use.
         */
        private String enc;
    
    
        /**
         * Current byte to char converter.
         */
        protected B2CConverter conv;
    
    
        /**
         * Associated Coyote request.
         */
        private Request coyoteRequest;
    
    
        /**
         * Buffer position.
         */
        private int markPos = -1;
    
    
        /**
         * Char buffer limit.
         */
        private int readLimit;
    
    
        /**
         * Buffer size.
         */
        private final int size;
    
    
        // ----------------------------------------------------------- Constructors
    
    
        /**
         * Default constructor. Allocate the buffer with the default buffer size.
         */
        public InputBuffer() {
    
            this(DEFAULT_BUFFER_SIZE);
    
        }
    
    
        /**
         * Alternate constructor which allows specifying the initial buffer size.
         *
         * @param size Buffer size to use
         */
        public InputBuffer(int size) {
    
            this.size = size;
            bb = ByteBuffer.allocate(size);
            clear(bb);
            cb = CharBuffer.allocate(size);
            clear(cb);
            readLimit = size;
    
        }
    
    }

      通过调试发现,当我的请求里没有中文时,CoyoteReader对象的InputBuffer属性的ByteBuffer的hb属性是能取到请求消息体的,而包含了中文则无法获取,protobuf直接转换报错了。

  • 相关阅读:
    异常总结
    反射
    面试题
    继承多态接口
    JAVA面向对象变成学习!
    学生管理系统---三层架构
    Secondary NameNode
    Hadoop之HDFS
    大数据相关概念二
    大数据相关概念
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/8857560.html
Copyright © 2011-2022 走看看