Pergunta

I am trying to consume a RESTful API using AA. My API receives email and password request parameters (not JSON!) and returns an APIKey (which I use Jackson2 to deserialize).

Ideally, I want to use a regular old Map<String, String> to send the email and password, but it appears AA requires me to use a MultiValueMap (which is a Map<K,List<V>>), or a custom class (Event, which has no source shown).

When using a MultiValueMap, an array is sent. I am not sending an array of email and passwords, I am sending a single email and password:

// LoginFragment.java            
MultiValueMap<String, String> credentials = new LinkedMultiValueMap<String, String>();
credentials.add("email", email);
credentials.add("password", password);
APIKey resp = userRest.login(credentials);


// UserRest.java
@Post("user/login")
public APIKey login(MultiValueMap credentials);

Which trips up my API, because it expects a String rather than an array of Strings.

So I'm thinking I have to create a custom Credentials object to hold my email and password, and somehow get it serialized to be sent to the server. Could someone help me out with this?

Foi útil?

Solução

Have you not looked at using the built in Authentication mechanisms that Android Annotations provides? Like Basic Auth or OAuth? This might be a cleaner solution.

https://github.com/excilys/androidannotations/wiki/Authenticated-Rest-Client

I have used the Basic Auth options - https://github.com/excilys/androidannotations/wiki/Rest%20API

You just need to add a method to your interface:

void setHttpBasicAuth(String username, String password);

Then call that before making the API call. There should be a similar option for OAuth.

EDIT: You can create a Login POJO to POST to your API:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Generated("org.jsonschema2pojo")
@JsonPropertyOrder({
        "name",
        "password"

})
public class Login{
    @JsonProperty("name")
    private String name;
    @JsonProperty("password")
    private String password;

}

and then in your API Interface you can do the following:

  @Post("user/login")
    public APIKey login(Login credentials);

This will then POST your data to the /user/login method. You might need to add an interceptor depending on what kind of data you wish to parse ie converters = { MappingJacksonHttpMessageConverter.class } etc.

Outras dicas

I know you already figured it out but check out the rest interceptor I use. Basically it attaches an api_key,access_token, and a hmac_sig to each request in the headers. Then you can validate the credientials server side

@EBean(scope = Scope.Singleton)
public class RestInterceptor implements ClientHttpRequestInterceptor {

private int requestCount = 0;

@Pref
MyPrefs_ myPrefs;

private RequestListener mRequestListener;

public interface RequestListener {
    void report(int count);
}

public void setOnRequestListener(RequestListener requestListener) {
    this.mRequestListener = requestListener;
}

public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution)
        throws IOException {

    if (mRequestListener != null) {
        requestCount++;
        mRequestListener.report(requestCount);
    }

    HttpHeaders headers = request.getHeaders();

    long unixTime = System.currentTimeMillis() / 1000L;

    headers.add("request_time", String.valueOf(unixTime));

    if (myPrefs.accessToken().exists()) {

        headers.add("access_token", myPrefs.accessToken().get());


        String hmacInput; //left this part out but basically do something unique to the request here and do the same on the other side.

        String hmacKey = myPrefs.accessToken().getOr("");


        try {
            String hmacSig = hmacSha1(hmacInput, hmacKey);

            headers.add("hmac_sig", hmacSig);

        }
        catch (InvalidKeyException e) {

            e.printStackTrace();
        }
        catch (NoSuchAlgorithmException e) {

            e.printStackTrace();
        }


    }
    if (myPrefs.userId().exists()) {
        headers.add("user_id", String.valueOf(myPrefs.userId().get()));
    }

    headers.add("api_key", "somerandomstring");

    ClientHttpResponse t = execution.execute(request, data);

    if (mRequestListener != null) {

        requestCount--;
        mRequestListener.report(requestCount);
    }

    return t;
}


public void resetRequestCount() {
    this.requestCount = 0;
}

public static String hmacSha1(String value, String key) throws UnsupportedEncodingException,
        NoSuchAlgorithmException, InvalidKeyException {
    String type = "HmacSHA1";
    SecretKeySpec secret = new SecretKeySpec(key.getBytes(), type);
    Mac mac = Mac.getInstance(type);
    mac.init(secret);
    byte[] bytes = mac.doFinal(value.getBytes());
    return bytesToHex(bytes);
}

private final static char[] hexArray = "0123456789abcdef".toCharArray();

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top