【问题标题】:Retrofit ignores query parameter改造忽略查询参数
【发布时间】:2017-04-20 08:58:15
【问题描述】:

我正在尝试使用 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}

编辑:这是我创建 Retrofit 对象的方式

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;
    }
}

【问题讨论】:

  • 有问题的工作 API 和您从代码中调用的 API 不同
  • 我编辑了一下,我认为它们应该是一样的。此外,如果您从错误消息中复制 url,它将在浏览器中运行

标签: android json gson retrofit


【解决方案1】:

当你执行这个请求时会附加两件事,首先你得到一个 302 响应,然后 OkHTTP 在 302 请求提供的新位置执行请求。第二个请求失败并返回 404 响应。我发现 302 响应的标头中有 2 个新 cookie:MC_DEVICE_IDMC_DEVICE_ID_EXT。所以我已经配置 OkHTTP(Retrofit 使用的请求引擎)将在 302 响应中收到的 cookie 发送到第二个请求,它可以工作!

这就是为什么它可以在网络浏览器中工作的原因,因为网络浏览器会存储返回的 cookie 并为第二个请求再次发送它。 OkHTTP默认没有这个机制,你需要在新请求中手动设置302响应中的cookies。

这是我的代码:

 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()

上一个答案

即使我正在调试,Retrofit/OkHTTP 堆栈跟踪也很难解码,但据我所知,url https://www.mediamarkt.de/de/product/productlistajax.json?categoryId=563612&amp;sort=topseller&amp;lazyLoading=true&amp;forceDeviceId=-111 返回一个 302 http 代码(暂时移动)并返回新地址响应头。

问题是标题location 提供的url 不包含查询参数forceDeviceId。它是 mediamarkt API 的一个错误,我建议您将此错误报告给 mediamarkt 开发人员,并暂时找到其他方式来发送此参数(我无能为力,我不懂德语)。

一些细节:

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

OkHTTP 通过调用这个来获取新位置:

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

(见这里RetryAndFollowUpInterceptor.java:301

location 的值为/de/product/productlistajax.json?categoryId=563612&amp;sort=topseller&amp;lazyLoading=true。如您所见,参数forceDeviceId 丢失了:/

【讨论】:

  • 感谢您的分析 - 我会努力解决的
  • 我做到了,但也许您对如何强制所有请求使用所需参数有其他提示?
  • 我正在尝试,如果我找到解决方案,我会更新我的答案,但它会有点脏
  • 在 302 响应头中,我发现是属性:MC_DEVICE_ID=-111; Path=/MC_DEVICE_ID_EXT=-111; Domain=mediamarkt.de; Path=/,也许它是链接的?
  • 是的!非常感谢
【解决方案2】:

您不需要在 GET 注释中包含查询参数。下面的代码对我来说很好

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

【讨论】:

  • 我尝试了一种或两种方法(不像我的 sn-p 那样结合起来),但没有一个在这里工作:/ 我更新了我的 sn-p 以使其更清晰
  • 奇怪,你是用HttpLoggingInterceptor拦截请求和响应体的吗?
  • 是的,我用Level.Body做了
  • 能否请您在这里发布您的调用方法,以及logcat中的请求和返回正文?
  • 向问题添加了更多信息。另请注意,我在复制粘贴时遇到了一些命名问题。
【解决方案3】:

将此参数与其他参数一起发送

   // 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
);

【讨论】:

  • 不确定你是否认真对待这个答案
猜你喜欢
  • 2015-05-13
  • 2019-05-15
  • 2014-11-01
  • 1970-01-01
  • 2022-06-28
  • 2016-08-12
  • 2020-08-07
  • 2017-08-23
相关资源
最近更新 更多