• 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直接转换报错了。

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/8857560.html
Copyright © 2020-2023  润新知