Модернизация: как указать параметры, разделенные запятыми, в запросе?

Я пытаюсь реорганизовать свой код, чтобы использовать Retrofit (из Volley) для некоторых вызовов API Foursquare, но не нашел подходящего примера, который показывает, как указать параметр запроса, который имеет 2 значения, разделенных запятой.

Моя базовая ссылка следующая:

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

Остальная часть моего URL выглядит так:

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

1-я реализация для моего интерфейса:

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.

2-я реализация (После просмотра некоторых ответов 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!");
        }
    });

В приведенной выше реализации метод error() вызывается всегда, поэтому в моем коде все еще что-то не так. Может кто-нибудь дать несколько советов, как правильно осуществить этот вызов? Я уверен, что проблема с "ll?" параметр.

Обновление: после включения регистрации это последний URL, который я получаю от 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 не нравится параметр ll в конце URL-адреса, и он должен быть явно размещен сразу после.. /v2/venues/explore, поскольку он работает нормально, когда отправляет запрос через браузер.

Какие-нибудь решения, чтобы обойти это ограничение от API?

3-я реализация (17.09.14) С предложением colriot мне удалось разрешить код ответа 400, который я получал с моей предыдущей реализацией. У меня все еще есть проблема со скоростью в GSON, поэтому я ищу предложения, как это исправить. В частности, моя реализация Retrofit требует больше времени для отображения моих результатов по сравнению с Volley, поэтому мне интересно, есть ли лучший способ реализовать обратный вызов.

Интерфейс Foursquare

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 call

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!");
        }
    });

В настоящее время проблема с вышеуказанной реализацией обратного вызова заключается в том, что для анализа и отображения результатов требуется больше времени (около 1 секунды), чем при использовании Volley. Блок GsonBuilder/Gson/JsonParser точно такой же, как в моем методе Volley onResponse (String response), за исключением этого промежуточного объекта response2, поэтому наиболее определенно этот промежуточный/дополнительный шаг является узким местом. Я ищу предложения о том, как лучше реализовать разбор Gson. Если это будет лучше, чем новый/отдельный вопрос, я сделаю это.

Ответ 1

Итак, как мы уже выяснили, проблема была в ?& typo. Но еще одна вещь, которую следует упомянуть, заключается в том, что Retrofit может принимать сложные объекты в качестве параметров вызова. Затем String.valueOf(object) вызывается для преобразования объекта в параметр Query/Path.

В вашем случае вы можете определить пользовательский класс следующим образом:

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.
  • Почему вы смешиваете JsonParser и Gson? Это разные инструменты для решения одной и той же проблемы.
  • Использовать механизм встроенного преобразователя 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!");
    }
});

Ответ 2

Просто введите urlencode аргументы, разделенные запятой, запятая =% 2

Ответ 3

Попробуйте добавить все параметры запроса (фиксированные и переменные) в допустимом порядке. Я имею в виду

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

и завершите вызов с помощью функции с фиксированными параметрами как константой

Ответ 4

Вы можете использовать @EncodedQuery:

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

Ответ 5

Если вы используете kotlin и у вас есть var items: List<String> который должен указываться в качестве параметра запроса.

Используйте этот метод для формирования строки:

fun itemsString() = items.joinToString(separator = ",")

Ваш URL-адрес должен быть таким:

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