2017-04-20 106 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