2017-04-20 88 views
0

我想解析與Retrofit和Gson的JSON文件。問題是URL接受參數forceDeviceId。如果此值設置爲-111,則可以恢復正確的文件。默認情況下,這個參數設置爲-1和一個404錯誤被拋出:改造忽略查詢參數

不工作 https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true&forceDeviceId=-1

工作 https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true&forceDeviceId=-111

我嘗試了兩種方法:硬編碼的API URL參數並使用查詢:

// either hardcoded in the URL 
@GET("de/product/productlistajax.json?forceDeviceId=-111") 
    Call<ProductListParent> loadProductList(
     @Query("categoryId") String categoryId, 
     @Query("sort") String sort, 
     @Query("lazyLoading") String lazyLoading, 
     // alternatively use this query 
     @Query("forceDeviceId") String forceDeviceId 
); 

但是這兩種方法都返回了404。所以我想知道我錯過了什麼讓它工作(就像它在瀏覽器中一樣)。我還認識到,在加載瀏覽器中的URL後,立即刪除該參數。那麼這是他們後端阻止的事情嗎?

這裏是方法,其中我打電話:

@Subscribe 
    public void getProductListRequest(MMSATServices.ProductListRequest request) { 
     final MMSATServices.ProductListResponse productListResponse = new MMSATServices.ProductListResponse(); 
     productListResponse.productListParent = new ProductListParent(); 

     Call<ProductListParent> call = liveApi.loadProductList(
       request.categoryId, request.sort, request.lazyLoading, request.forceDeviceId); 
     call.enqueue(new Callback<ProductListParent>() { 
      @Override 
      public void onResponse(Call<ProductListParent> call, Response<ProductListParent> response) { 
       productListResponse.productListParent = response.body(); 
       bus.post(productListResponse); 
      } 

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

這是錯誤消息我得到:

Response{protocol=http/1.1, code=404, message=Not Found, url=https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true} 

編輯:這裏是我創建的改造對象

private static Retrofit createMMSATService(String baseUrl) { 
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 

    OkHttpClient client = new OkHttpClient.Builder() 
      .addInterceptor(interceptor) 
      .cookieJar(new CustomCookieJar()) 
      .build(); 

    Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl(baseUrl) 
      .client(client) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .build(); 

    return retrofit; 
} 

CustomCookieJAr類別:

public class CustomCookieJar implements CookieJar { 

    private List<Cookie> cookies = new ArrayList<>(); 

    @Override 
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { 
     if (cookies != null) { 
      this.cookies = cookies; 
     } 
    } 

    @Override 
    public List<Cookie> loadForRequest(HttpUrl url) { 
     return cookies; 
    } 
} 
+0

正在討論的工作API和你從代碼中調用的API是不同的 –

+0

我編輯了一下,我認爲它們應該是相同的。此外,如果你從錯誤消息中複製url,它將在瀏覽器中工作 – 4ndro1d

回答

2

2周的事情,當你執行這個要求,首先你會得到一個302響應,然後OkHTTP在新的執行請求追加位置由302請求提供。第二個請求失敗,出現404響應。我發現302響應的標題中有2個新的Cookie:MC_DEVICE_IDMC_DEVICE_ID_EXT。所以我配置了OkHTTP(Retrofit使用的請求引擎)將302響應中收到的cookies發送到第二個請求,並且它可以工作!

這就解釋了爲什麼它在Web瀏覽器中工作,因爲Web瀏覽器存儲返回的cookie並將其發送給第二個請求。 OkHTTP默認沒有這種機制,你必須在新請求中手動設置來自302響應的cookie。

有我的代碼:

Retrofit retrofit = new Retrofit.Builder() 
      .client(new OkHttpClient.Builder() 
        .cookieJar(new CookieJar() { 
         private List<Cookie> cookies = new ArrayList<>(); 
         @Override 
         public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { 
          if (cookies != null) { 
           this.cookies = cookies; 
          } 
         } 

         @Override 
         public List<Cookie> loadForRequest(HttpUrl url) { 
          return cookies; 
         } 
        }).build()) 
      .baseUrl("https://www.mediamarkt.de/") 
      .build() 

以前的答案

改造/ OkHTTP堆棧跟蹤真的很難,即使我調試,但據我所看到的解碼,網址https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true&forceDeviceId=-111返回一個302 http代碼(暫時移動)並返回響應頭中的新地址。

問題是由標題location提供的url不包含查詢參數forceDeviceId。它是來自mediamarkt API的一個bug,我建議你向mediamarkt開發者報告這個bug,並且找到另一種方法發送這個參數(我無法幫助更多,我不明白德語語言)。

一些細節:

在第一次調用返回的響應302: Response{protocol=http/1.1, code=302, message=Found, url=https://www.mediamarkt.de/de/product/productlistajax.json?forceDeviceId=-111&categoryId=563612&sort=topseller&lazyLoading=true}

通過調用這個OkHTTP獲得新的位置:

String location = userResponse.header("Location"); 

(見這裏RetryAndFollowUpInterceptor.java:301

location的值是/de/product/productlistajax.json?categoryId=563612&sort=topseller&lazyLoading=true。正如你可以看到缺少參數forceDeviceId:/

+0

感謝您的分析 - 我會盡力弄清楚 – 4ndro1d

+0

太好了,您能否將我的回答標記爲已接受的答案? – RxVincent

+0

我做了,但也許你有另一個提示如何強制所有請求使用所需的參數? – 4ndro1d

0

您不需要在GET註釋中包含查詢參數。下面的代碼爲我工作的罰款

@GET("de/product/productlistajax.json") 
Call<AppConfigParent> loadAppConfigParent(@Query("forceDeviceId") String forceDeviceId); 
+0

我嘗試了兩種或兩種方法(不是兩者結合就像在我的代碼段中),也沒有在這裏工作:/我更新了我的代碼片段以使其更清晰 – 4ndro1d

+0

奇怪,你是否使用HttpLoggingInterceptor攔截請求和響應正文? –

+0

是的,我使用了'Level.Body' – 4ndro1d

-1

發送此參數與其他參數

// either hardcoded in the URL 
@GET("de/product/productlistajax.json") 
    Call<ProductListParent> loadProductList(
     @Query("forceDeviceId") String forceDeviceId, 
     @Query("categoryId") String categoryId, 
     @Query("sort") String sort, 
     @Query("lazyLoading") String lazyLoading, 
     // alternatively use this query 
     @Query("forceDeviceId") String forceDeviceId 
); 
+0

不知道您是否認真對待這個問題 – 4ndro1d