【问题标题】:Retrofit: How to specify comma-separated parameters in request?改造:如何在请求中指定逗号分隔的参数?
【发布时间】:2014-11-03 14:02:13
【问题描述】:

我正在尝试重构我的代码,以便将 Retrofit(来自 Volley)用于一些 Foursquare API 调用,但没有找到一个合适的示例来说明如何指定一个查询参数,该参数具有用逗号分隔的 2 个值。

我的基本网址如下:

public static final String VENUES_BASE_URL = "https://api.foursquare.com/v2/venues";

我的网址的其余部分是这样的:

"?ll=40.7,50.2&limit=50&radius=25000&v=20140909&venuePhotos=1&oauth_token=xxyyxx";

我的界面的第一个实现:

public interface Fourquare {
    @GET("/explore?ll={p1},{p2}&limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx")
    Response getVenues(@Path("p1") String param1,
                   @Path("p2") String param2);

}

然后发出这样的请求:

RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(ConfigConstants.VENUES_BASE_URL)
            .build();

    Fourquare fourquare = restAdapter.create(Fourquare.class);
    Response myResponse = fourquare.getVenues("50", "75");

但是,上面给了我以下错误:

retrofit.RetrofitError: Fourquare.getVenues: URL query string "ll={p1},{p2}&limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx" must not have replace block.

第二次实现(在查看了一些使用查询参数的 SO 响应之后。注意:一旦我弄清楚 ll? 参数调用,我将把令牌作为参数):

@GET("/explore&limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx")
void getVenues(@Query("ll") String ll,
                      Callback<String> cb);

实际调用如下:

fourquare.getVenues("50,75", new Callback<String>() {
        @Override
        public void success(String s, Response response) {
            Log.d(TAG, "Successful run!");
        }

        @Override
        public void failure(RetrofitError error) {
            Log.d(TAG, "Failed run!");
        }
    });

通过上述实现,failure() 方法总是被调用,所以我的代码仍然有问题。有人可以就执行此调用的正确方法提出一些建议吗?我很确定问题出在“ll?”上。范围。

更新: 开启日志记录后,这是我从 Retrofit 获得的最终网址: https://api.foursquare.com/v2/venues/explore&limit=50&radius=25000&v=20140909&venuePhotos=1&oauth_token=xxyyxx?ll=30.26%2C-97.74

看起来 Foursquare 服务器不喜欢 url 末尾的 ?ll 参数,它必须明确放置在 ../v2/venues/explore 之后,因为当通过浏览器。

有什么解决方案可以从 API 中绕过这个限制?

第三次实施(2014 年 9 月 17 日) 借助 colriot 的建议,我能够解决以前实施时遇到的 400 响应代码。我仍然遇到 GSON 的速度问题,因此正在寻找有关如何解决该问题的建议。具体来说,与 Volley 相比,我的 Retrofit 实现需要更长的时间来显示我的结果,所以我想知道是否有更好的方法来实现回调。

四方界面

public interface Fourquare {   
    @GET("/explore?limit=50&radius=25000&v=20140909&venuePhotos=1&oauth_token=xxyyxx")
    void getVenues(@Query("ll") String ll,
                Callback<Object> cb);
}

RestAdapter 调用

RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(ConfigConstants.VENUES_BASE_URL)
            .build();

    Foursquare foursquare = restAdapter.create(Foursquare.class);

foursquare.getVenues("30.26,-97.74", new Callback<Object>() {
        @Override
        public void success(Object o, Response response) {
            Log.d(TAG, "Success!");
            // Parse response
            GsonBuilder gsonBuilder = new GsonBuilder();
            Gson gson = gsonBuilder.create();
            JsonParser parser = new JsonParser();
            String response2 = gson.toJson(o);
            JsonObject data = parser.parse(response2).getAsJsonObject();

            // Populate data model
            MetaResponse metaResponse = gson.fromJson(data.get("meta"), MetaResponse.class);
            VenuesExploreResponse myResponse = gson.fromJson(data.get("response"), VenuesExploreResponse.class);                

            // Store results from myResponse in List
        }

        @Override
        public void failure(RetrofitError error) {
            Log.d(TAG, "Failures!");
        }
    });

上述回调实现的当前问题是解析和显示结果需要比 Volley 更长的时间(大约 1 秒)。 GsonBuilder/Gson/JsonParser 块与我的 Volley onResponse(String response) 方法完全相同,除了那个中间“response2”对象,所以这个中间/额外步骤肯定是瓶颈。我正在寻找有关如何更好地实现 Gson 解析的建议。如果这可能更适合作为一个新的/单独的问题,我会这样做。

【问题讨论】:

    标签: android foursquare retrofit


    【解决方案1】:

    如果您使用的是 kotlin,并且您有 var items: List&lt;String&gt; 应该作为查询参数。

    使用此方法组成字符串:

    fun itemsString(items: List<String>) = items.joinToString(separator = ",")
    

    你的 url 查询应该是这样的:

    @Query(value = "items", encoded = true) String items
    

    【讨论】:

      【解决方案2】:

      所以,我们已经发现问题出在? -> &amp; 错字。但是还要提一提的是,Retrofit 可以接受复杂的对象作为调用参数。然后将调用String.valueOf(object) 将对象转换为查询/路径参数。

      在您的情况下,您可以像这样定义自定义类:

      class LatLng {
        private double lat;
        private double lng;
      
        ...
      
        @Override public String toString() {
          return String.format("%.1f,%.1f", lat, lng);
        }
      }
      

      然后像这样重构你的端点方法:

      @GET("/explore?limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx")
      void getVenues(@Query("ll") LatLng ll, Callback<String> cb);
      

      关于解析答案:

      • 永远不要在回调中创建Gson 对象。简直太重量级了。使用你提供给RestAdapter的那个。
      • 你为什么混合JsonParserGson?它们是针对基本相同问题的不同工具。
      • 利用 Retrofit 的内置转换器机制 ;)

      从文字到代码:

      public class FoursquareResponse<T> {
        private MetaResponse meta;
        private T response;
        // getters
      }
      

      总共:

      @GET("/explore?limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx")
      void getVenues(@Query("ll") LatLng ll, Callback<FoursquareResponse<VenuesExploreResponse>> cb);
      
      ...
      
      foursquare.getVenues(LatLng.valueOf(30.26, -97.74), new Callback<FoursquareResponse<VenuesExploreResponse>>() {
          @Override 
          public void success(FoursquareResponse<VenuesExploreResponse> r, Response response) {
              MetaResponse metaResponse = r.getMeta;
              VenuesExploreResponse myResponse = r.getResponse();
      
              // Store results from myResponse in List
          }
      
          @Override
          public void failure(RetrofitError error) {
              Log.d(TAG, "Failures!");
          }
      });
      

      【讨论】:

      • 感谢您的建议。我肯定会重构它。关于 Gson 解析部分的任何建议?我应该为此提交一个新问题吗?
      • @cavega 抱歉,我只是第一次没有注意到第三个音符。
      • 感谢您详细解释如何使用 Retrofit 的内置 Gson 解析功能。我从前一段时间提出的一个 Gson 特定问题的答案中获取了整个 JsonParser/Gson 代码。我肯定会深入研究 Retrofit 的代码。
      • @cavega 欢迎!此外,如果您想自定义解析参数,例如 API 为您提供带下划线的字段名称,但您在代码中使用 camelCase,则只需配置自定义 Gson 实例并将其传递给 RestAdapter.Builder
      【解决方案3】:

      只需对逗号分隔的参数进行 urlencode,逗号 = %2

      【讨论】:

      • @JakeWharton 我想知道问题是否可能是 Retrofit 在发出实际 http 请求时对参数进行排序的顺序。 Retrofit 是否在基本 url 之后插入查询参数?还是在完整 URL 的末尾?我目前在发布的代码上得到 400。
      • 它将动态的附加到静态查询参数的末尾。您可以打开日志记录以查看完整的传出 URL。
      • @JakeWharton 我根据您的日志记录建议更新了我的问题。服务器不喜欢 URL 末尾的 fill 参数。有关如何绕过此限制的任何建议?
      • @cavega 将/explore 之后的&amp; 替换为?
      • @colriot 不敢相信我错过了那个错字。这解决了 400 响应问题。谢谢!在我的帖子上写下一个剩余问题的更新,以防你想知道答案。
      【解决方案4】:

      尝试按有效顺序添加所有查询参数(固定和可变)。 我的意思是

      @GET("/explore")
      Response getVenues(@Query("ll") ll, @Query("limit") limit, @Query("radius") radius, @Query("v") v, @Query("venuePhotos") venuePhotos, @Query("oauth_token") oauth_token);
      

      并使用具有固定参数的函数包装调用作为常量

      【讨论】:

      • 我能够使用@colriot 建议重新解决我的 400 代码,但是是的,我计划通过将大部分(如果不是全部)参数作为查询参数来重构界面。感谢您的反馈。
      【解决方案5】:

      你可以使用@EncodedQuery

      @GET("/explore&limit=50&radius=25000&v=20140905&venuePhotos=1&oauth_token=xxyyxx")
      void getVenues(@EncodedQuery("ll") LatLng ll, Callback<String> cb);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-06
        • 1970-01-01
        • 2019-08-19
        • 1970-01-01
        相关资源
        最近更新 更多