如何使用 Android 中的 HttpURLConnection class 将 JSON 对象发送到 Web 服务?

How to send a JSON Object to a web service using HttpURLConnection class in Android?

我使用 RESTEasy API 开发了 Rest Web 服务,它接受 Json 对象并进行一些处理。我为此编写了两个客户端,一个 Android 客户端和一个普通的 Java 程序客户端。这两个除了连接建立部分外基本相同,功能相同。

我在 Java 客户端中使用 RESTEasy Connection APIs,在 Android 应用程序中使用 HttpURLConnection,因为它是推荐的(而不是旧的 Apache HttpClient)。

在 Java 客户端中一切正常,但在 Android 应用程序中却不一样。当我 运行 Android 客户端时,我得到这个异常 "com.fasterxml.jackson.core.jsonparseexception: unexpected end-of-input"。

我不明白哪里出了问题,所以我尝试使用 Chrome Advanced Rest Client 进行一些调试测试,以检查 Web 服务是否一切正常。因此,我从两个客户端获取了 Json 字符串的副本,并作为对 Web 服务的 POST 请求执行,它运行良好。

在进行更多测试后,我认为这是因为 HttpURLConnection 连接使用 OutputStream 写入数据并将二进制流发送到端点,而 Web 服务无法正确读取 Web 服务。这就是我能想到的。 (我用 Jasonlint 工具测试了 Json 字符串,它们都是有效的。)

有人可以向我解释我做错了什么或如何使用 HttpURLConnection 正确调用 Web 服务吗?提前致谢。

Web Service Code

@Path("/upload")
public class ImageTransferHandler {

@POST
@Path("/jsonString")
@Consumes("application/json")
public Response upload (ImageData jsonString){

    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Image.class, new ImageDeserializer());

    Gson gson = builder.create();

    //Image image = gson.fromJson(jsonString, Image.class);
    ImageData image = jsonString;

    System.out.println("sys out : Name: " + image.getImageName() + " \nEncoded Image String : " + image.getEncodedImageString());

    // Decode the base64 String and get the byte array
    byte[] imageBytes = image.getEncodedImageString().getBytes();

    byte[] decodedBytes = Base64.decodeBase64(imageBytes);

    try {

        // Convert the bytes to an image and store
        ImageConverter ic = new ImageConverter();

        ic.bytesToImage(decodedBytes, "C:\Users\yomal.ds\Desktop\test\output.jpg");

    } catch (Exception e) {
        // TODO Auto-generated catch block

        e.printStackTrace();
    }

    try{
        /**
         * Checksum Code Start
         * */
        MessageDigest md1 = MessageDigest.getInstance("SHA-256");
        md1.update(imageBytes);
        byte[] mdbytes1 = md1.digest();

        StringBuffer hexedHashB64 = new StringBuffer();

        for (int i = 0; i < mdbytes1.length; i++) {
            hexedHashB64.append(Integer.toString((mdbytes1[i] & 0xff) + 0x100, 16).substring(1));
        }

        System.out.println("HEXED HASH (Base64) : " + hexedHashB64.toString());
        /**
         * Checksum Code End
         * */


        /**
         * Checksum Code Start
         * */
        MessageDigest md2 = MessageDigest.getInstance("SHA-256");
        md2.update(decodedBytes);
        byte[] mdbytes2 = md2.digest();

        StringBuffer hexedHash = new StringBuffer();

        for (int i = 0; i < mdbytes2.length; i++) {
            hexedHash.append(Integer.toString((mdbytes2[i] & 0xff) + 0x100, 16).substring(1));
        }

        System.out.println("HEXED HASH (Original) : " + hexedHash.toString());
        /**
         * Checksum Code End
         * */

    } catch (NoSuchAlgorithmException e) {
        System.err.println("Algorithm is not correct");
        e.printStackTrace();
    }
    return Response.status(200).entity(image.toString()).build();
}   

}

Android Client Code (Connection Part)

try {
                      Log.e("Upload", "Making connection");
                      String url= "http://192.168.43.2:8080/RestEasyWS/rest/upload/jsonString";
                      URL urlObj = new URL(url);
                      Log.e("Upload", "Opening tunnel");
                      HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
                      Log.e("Upload", "Tunnel Opened");
                      con.setDoInput(true);
                      con.setDoOutput(true);
                      con.setChunkedStreamingMode(0);
                      con.setRequestProperty("Content-Type", "application/json");
                      con.setRequestProperty("Accept", "application/json");
                      con.setRequestProperty("Accept-Encoding","gzip, deflate");
                      con.setRequestProperty("Accept-Language","en-US,en;q=0.8");
                      con.setRequestMethod("POST");
                      Log.e("Upload", "Connection Made");
                      OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());

                      Log.e("Upload", "Uploading...");
                      osw.write(jsonString);
                      Log.e("Upload", "Uploaded...");

                      Log.e("Upload", "Server Response : " + con.getResponseCode() + " - " + con.getResponseMessage() );

                      osw.flush();
                      osw.close();

                  } catch (IOException e) {
                      Log.e("Connection", "Error In Opening a Connection");
                      e.printStackTrace();
                  }

Stack Trace of the Error

Mar 16, 2016 12:16:00 PM org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher processApplication
INFO: RESTEASY002225: Deploying javax.ws.rs.core.Application: class com.informatics.webservice.MessageApplication
Mar 16, 2016 12:16:00 PM org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher processApplication
INFO: RESTEASY002220: Adding singleton resource com.informatics.webservice.imagetransfer.ImageTransferHandler from Application class com.informatics.webservice.MessageApplication
Mar 16, 2016 12:16:04 PM org.jboss.resteasy.core.ExceptionHandler handleFailure
ERROR: RESTEASY002005: Failed executing POST /upload/jsonString
org.jboss.resteasy.spi.ReaderException: com.fasterxml.jackson.databind.JsonMappingException: Unexpected end-of-input in VALUE_STRING
at [Source: org.apache.catalina.connector.CoyoteInputStream@9ceeccd; line: 1, column: 7236430]
at [Source: org.apache.catalina.connector.CoyoteInputStream@9ceeccd; line: 1, column: 23] (through reference chain: com.informatics.webservice.commonobjects.ImageData["encodedImageString"])
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:184)
at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:91)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:114)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:295)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:249)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:236)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:395)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:202)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:221)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Unexpected end-of-input in VALUE_STRING
at [Source: org.apache.catalina.connector.CoyoteInputStream@9ceeccd; line: 1, column: 7236430]
at [Source: org.apache.catalina.connector.CoyoteInputStream@9ceeccd; line: 1, column: 23] (through reference chain: com.informatics.webservice.commonobjects.ImageData["encodedImageString"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:339)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:299)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1511)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:262)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1534)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:944)
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.readFrom(ResteasyJackson2Provider.java:121)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:61)
at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.readFrom(ServerReaderInterceptorContext.java:60)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:53)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(GZIPDecodingInterceptor.java:59)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:55)
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:151)
... 30 more

你在 rest 客户端上测试过 api 了吗(post 伙计),看来你的远程服务器有问题。

我没有使用 OutputStreamWriter,而是使用了 Output Stream 并使用 UTF-8 字符集对字节进行了编码。

OutputStream os= con.getOutputStream();
os.write(jsonString.getBytes("UTF-8"));
os.flush;
os.close;