2017-04-10 114 views
4

我在Android上使用Retrofit2與OkHttp進行HTTP請求。在這裏,我正在做一個帶有文檔上傳的POST請求。我遇到了以下錯誤:Android with Retrofit2 OkHttp3 - 多部分POST錯誤

D/OkHttp: <-- 500 Server Error http://api.drivewealth.io/v1/documents (4289ms) 
D/OkHttp: Date: Tue, 11 Apr 2017 03:29:48 GMT 
D/OkHttp: Cache-Control: must-revalidate,no-cache,no-store 
D/OkHttp: Content-Type: text/html; charset=ISO-8859-1 
D/OkHttp: Server: Jetty(9.2.17.v20160517) 
D/OkHttp: Content-Length: 9323 
D/OkHttp: Connection: keep-alive 
D/OkHttp: <html> 
D/OkHttp: <head> 
D/OkHttp: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 
D/OkHttp: <title>Error 500 Server Error</title> 
D/OkHttp: </head> 
D/OkHttp: <body><h2>HTTP ERROR 500</h2> 
D/OkHttp: <p>Problem accessing /v1/documents. Reason: 
D/OkHttp: <pre> Server Error</pre></p><h3>Caused by:</h3><pre>org.apache.cxf.interceptor.Fault: Couldn&apos;t determine the boundary from the message! 
D/OkHttp:  at org.apache.cxf.interceptor.AttachmentInInterceptor.handleMessage(AttachmentInInterceptor.java:60) 
D/OkHttp:  at org.apache.cxf.jaxrs.ext.MessageContextImpl.createAttachments(MessageContextImpl.java:279) 
D/OkHttp:  at org.apache.cxf.jaxrs.ext.MessageContextImpl.get(MessageContextImpl.java:77) 
D/OkHttp:  at org.apache.cxf.jaxrs.impl.tl.ThreadLocalMessageContext.get(ThreadLocalMessageContext.java:42) 
D/OkHttp:  at org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils.getMultipartBody(AttachmentUtils.java:114) 
D/OkHttp:  at org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils.getAttachments(AttachmentUtils.java:119) 

完整的調試日誌上傳here

的API服務器需要這種格式的HTTP POST多要求:

enter image description here

我的代碼片段作爲如下:

1)創建改造處理程序類:

Interceptor headerInterceptor = new Interceptor() { 
    @Override 
    public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException { 
     Request original = chain.request(); 

     String sessionKey = JStockApplication.instance().getTradingOptions().getSessionKey(); 

     okhttp3.Request request = original.newBuilder() 
       //.header("Accept", "application/json") 
       .header("Content-Type", "multipart/form-data") 
       .header("x-mysolomeo-session-key", sessionKey) 
       .method(original.method(), original.body()) 
       .build(); 

     return chain.proceed(request); 
    } 
}; 

OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); 

// add static common headers 
httpClient.addInterceptor(headerInterceptor); 

// add Logging for development, Log Level: NONE, BASIC, HEADERS, BODY 
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor() 
     .setLevel(HttpLoggingInterceptor.Level.BODY); 
httpClient.addInterceptor(logInterceptor); 

Retrofit.Builder builder = new Retrofit.Builder() 
     .baseUrl("http://api.drivewealth.io/v1/") 
     .addConverterFactory(GsonConverterFactory.create()) 
     .client(httpClient.build()); 

Retrofit retrofit = builder.build(); 

DriveWealthApi api = retrofit.create(DriveWealthApi.class); 

2)改造方法接口類:

import okhttp3.MultipartBody; 
import okhttp3.RequestBody; 
import okhttp3.ResponseBody; 
import retrofit2.Call; 
import retrofit2.http.Body; 
import retrofit2.http.Multipart; 
import retrofit2.http.POST; 
import retrofit2.http.Part; 

public interface DriveWealthApi { 
    @Multipart 
    @POST("documents") 
    Call<ResponseBody> addDocument(
      @Part("token") RequestBody token, 
      @Part("documentType") RequestBody documentType, 
      @Part MultipartBody.Part file); 
} 

3)在我的片段類,POST請求被調用的的onCreate():

public class AddDocumentTaskFragment extends Fragment implements Callback<ResponseBody> { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     ........ 
     ........ 

     Bundle bundle = this.getArguments(); 
     String userID = bundle.getString(INTENT_EXTRA_USER_ID); 
     String docType = bundle.getString(INTENT_EXTRA_DOCUMENT_TYPE); 
     String fileUri = bundle.getString(INTENT_EXTRA_FILE_URI); 
     Uri uri = Uri.parse(fileUri); 

     String filePath = MyUtils.getPath(this.getActivity(), uri); 

     if (filePath == null || filePath.isEmpty()) { 
      return; 
     } 

     final File myFile = new File(filePath); 
     MediaType mediaType = MediaType.parse(getActivity().getContentResolver().getType(uri)); 

     if (myFile == null) { 
      return; 
     } 

     // create RequestBody instance from file 
     RequestBody requestFile = RequestBody.create(mediaType, myFile); 

     // MultipartBody.Part is used to send also the actual file name 
     MultipartBody.Part fileBody = MultipartBody.Part.createFormData("documentImage", myFile.getName(), requestFile); 

     // add another part within the multipart request 
     RequestBody tokenBody = RequestBody.create(okhttp3.MultipartBody.FORM, userID); 

     RequestBody docTypeBody = RequestBody.create(okhttp3.MultipartBody.FORM, docType); 

     // params: token, documentType, file 
     this.call = driveWealthApi.addDocument(tokenBody, docTypeBody, fileBody); 
     this.call.enqueue(this); 
    } 

    @Override 
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
     ..... 
    } 

    @Override 
    public void onFailure(Call<ResponseBody> call, Throwable t) { 
     ...... 
    } 

任何想法什麼地方出了錯這裏?謝謝!

+0

嗨!我無法檢查您的DriveWealth API,但是,看起來它在其示例中具有奇怪的邊界定義(邊界= ---- WebKitFormBoundary7MA4YWxkTrZu0gW,與流中邊界相同),請閱讀http:/ /stackoverflow.com/questions/3508252/what-is-the-in-multipart-form-data – BNK

+2

@BNK,感謝您的反饋。我在下面解決了我的問題,再次感謝您的幫助! :) –

回答

4

在參考Retrofit 2 can't upload a file with two additional separate string parameters之後,我根據@TommySM的建議實施。我有我的問題,這解決了:

// create RequestBody instance from file 
RequestBody requestFile = RequestBody.create(
      mediaType, 
      myFile); 

// MultipartBody.Part is used to send also the actual file name 
MultipartBody.Part file = MultipartBody.Part.createFormData(
      "documentImage", 
      myFile.getName(), 
      requestFile); 

// add another part within the multipart request 
RequestBody token = RequestBody.create(
      MediaType.parse("text/plain"), // Fixed here 
      //okhttp3.MultipartBody.FORM, => PROBLEMATIC 
      userID); 

RequestBody docType = RequestBody.create(
      MediaType.parse("text/plain"), // Fixed here 
      //okhttp3.MultipartBody.FORM, => PROBLEMATIC 
      docTypeStr); 

// token, documentType, file 
this.call = driveWealthApi.addDocument(token, docType, file); 

這些字符串參數應指定爲Content-Type: text/plain,而不是Content-Type: multipart/form-data

詳細見截圖:

1)有問題的POST

enter image description here

2)正確的POST

enter image description here