2

It seems that I am unable to set arbitrary query parameters to a @Get declaration

My endpoint looks like

http://api.lmiforall.org.uk/api/v1/ashe/estimateHours?soc=2349&coarse=true

There are a non trivial amount of parameters to this query, is there a declaration I can use to indicate this to the @Rest interface?

I tried declaring it as this, but it complains about fields being unused.

@Get("estimateHours")
ASHEFilterInfo GetEstimateHours( int soc, boolean coarse, String filters, String breakdown);

java: @org.androidannotations.annotations.rest.Get annotated method has only url variables in the method parameters
Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91

5 Answers5

1

Look at AA cookbook.

Try this (not tested):


@Rest(rootUrl = "http://api.lmiforall.org.uk/api/v1/ashe")
public interface MyService {

    @Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdonw}&filters={filters}")
    ASHEFilterInfo GetEstimateHoursFiltered( int soc, boolean coarse, String filters, String breakdown);

  @Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdonw}")
    ASHEFilterInfo GetEstimateHours( int soc, boolean coarse, String breakdown);

}
jmvivo
  • 2,653
  • 1
  • 16
  • 20
1

When I needed to create @Get request with many dynamic parameteres, and some of them could be duplicated, I had resolved that problem so:

@Rest(rootUrl = "http://example.com:9080/",
        converters = { GsonHttpMessageConverter.class },
        interceptors = { ApiInterceptor.class })
public interface ExampleApi {

   @Get("content/home/product-type/list?{filters}&domain={domain}") //filters is String like "param1=value1&param1=value2&param3=value3"
   ProductTypeListResponse getProductTypeList(int domain, String filters);

 }

public class ApiInterceptor implements ClientHttpRequestInterceptor {

    private static final String TAG = ApiInterceptor.class.getSimpleName();

    @Override
    public ClientHttpResponse intercept(final HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final QueryMultiParamsHttpRequest modifiedRequest = new QueryMultiParamsHttpRequest(request);
        return execution.execute(modifiedRequest, body);
    }
}

public class QueryMultiParamsHttpRequest implements HttpRequest {

    private static final String TAG = QueryParametersBuilder.class.getSimpleName();
    private HttpRequest httpRequest;

    public QueryMultiParamsHttpRequest(final HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    @Override
    public HttpMethod getMethod() {
        return httpRequest.getMethod();
    }

    @Override
    public URI getURI() {
        final URI originalURI = httpRequest.getURI();
        final String query = originalURI.getQuery() != null ? originalURI.getQuery().replace("%3D", "=").replace("%26", "&") : null;
        URI newURI = null;
        try {
            newURI = new URI(originalURI.getScheme(), originalURI.getUserInfo(), originalURI.getHost(), originalURI.getPort(), originalURI.getPath(),
                    query, originalURI.getFragment());
        } catch (URISyntaxException e) {
            Log.e(TAG, "Error while creating URI of QueryMultiParamsHttpRequest", e);
        }
        return newURI;
    }

    @Override
    public HttpHeaders getHeaders() {
        return httpRequest.getHeaders();
    }
}

So, I created a wrapper for HttpRequest, that can decode symbols "=" and "&". And this wrapper replaces original HttpRequest in ApiInterceptor. This is a little hacky solution, but it works.

John Smith
  • 605
  • 4
  • 14
0

I ran into this same issue and came up with a another solution that while far from ideal, works. The particular problem I was trying to solve was handling "HATEOAS" links.

What I ended up doing was creating a separate class called HATEOASClient to contain endpoint methods that would not escape the HATEOAS links passed in as params. To do that I basically just looked at an auto generated endpoint method and coped/tweaked the body in my implementation.

These methods use the same RestTemplate instance AndroidAnnotations sets up so you still get access to all the general setup you do on the RestTemplate.

For example:

    public ResponseEntity<Foo> postFoo(Foo foo) {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.set(RestHeader.AUTH_TOKEN_HEADER, getClient().getHeader(RestHeader.AUTH_TOKEN_HEADER));
            httpHeaders.set(RestHeader.ACCEPT_LANGUAGE_HEADER, getClient().getHeader(RestHeader.ACCEPT_LANGUAGE_HEADER));
            httpHeaders.setAuthorization(authentication);
            HttpEntity<Foo> requestEntity = new HttpEntity<>(null, httpHeaders);
            HashMap<String, Object> urlVariables = new HashMap<>();
            urlVariables.put("link", foo.getLinks().getFooCreate().getHref());
            URI expanded = new UriTemplate(getClient().getRootUrl().
                    concat(API_VERSION + "{link}")).expand(urlVariables);
            final String url;
            try {
                url = URLDecoder.decode(expanded.toString(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            return getClient().getRestTemplate().
                    exchange(url, HttpMethod.POST, requestEntity, Foo.class, urlVariables);
    }
Nick
  • 8,181
  • 4
  • 38
  • 63
0

If all parameters is required you can use @Path annotation.

@Rest(rootUrl = "http://api.lmiforall.org.uk/api/v1/ashe")
public interface MyService {

    @Get("/estimateHours?soc={soc}&coarse={coarse}&breakdown={breakdown}&filters={filters}")
    ASHEFilterInfo GetEstimateHours(@Path int soc, @Path boolean coarse, @Path String breakdown, @Path String filters);

}

If one of the parameters is optional, there isn't yet a solution that can you can easily pass parameters using Android Annotations. But anybody can contribute to better Android Annotations.

Marco Altran
  • 376
  • 2
  • 9
-1

if you define the params for each method then you need to provide them in each request. I thought this was sort of over kill too so what I did was just make a generic get/post request in my api client then just manually enter the values, if you don't define the root url I suppose you could use the QueryStringBuilder class and build the uri that way.

@Rest(rootUrl = "https://path/to/api/", converters = { FormHttpMessageConverter.class,
    GsonHttpMessageConverter.class, ByteArrayHttpMessageConverter.class })

    public interface ApiClient {

@Get("{uri}")
JsonElement apiGet(String uri);

@Post("{uri}")
JsonObject apiPost(String uri,MultiValueMap data);

RestTemplate getRestTemplate();

void setRootUrl(String rootUrl);

void setRestTemplate(RestTemplate restTemplate);

}

Example usage

JsonElement resp = apiClient.apiGet("method/?random_param=1&another_param=test);

It's not as clean but can be dynamic

Brian
  • 4,328
  • 13
  • 58
  • 103
  • 2
    I'm curious how you got this to work since AA/RestTemplate will automatically escape this param into something like "method/%3Frandom_param...." – Nick Jul 09 '15 at 16:15
  • I think you're right you'll have to avoid underscores OR possibly unencode the query string paramaters server side – Brian Jul 09 '15 at 19:46