The Premise
I'm working on a simple app where I want to list out a user's GitHub repos in a RecyclerView. I'm using this as my endpoint while building this.
The Problem
The problem I'm facing is that the GitHub API returns only 30 repos in one go. To get more, I can append a per_page=100 (100 is the maximum) to my query string; But, what do we do about users with more than 100 repos?
The solution the API docs provide is to get the next; url from the "Link" response header to make a second API call.
How does one go about this? Thanks!
The Response class will let you access the headers. The GitHub client would have a method like:
#GET("/search/code")
Observable<Response<T>> getUser(...)
Inside onNext(), you'd do something like:
#Override
public void onNext(Response<T> response) {
String next = response.headers().get("next");
}
Related
Lately I have been facing a problem that I wasn't able to came out with a solution, I am building an android application following the Clean Architecture and everything was going fine until I had to think about the authentication role.
I have this structure (layers) on my app:
[ui](activities and fragments) -> [presentation](view models) -> [domain](use case) -> data -> [remote, cache, database].
Now, let's suppose that I want to log in into my app, first I'll go through the login screen and put users credentials, after that I'll call the LoginViewModel and then the LoginUseCase passing the email and password. In its turn, the use case will call the repository, let's say, authenticate and then I'll make a request to the backend with the credentials, if everything's ok then I'll receive back a token that I should store in some fashion, the problem start here, I've created a interceptor that is responsible to get the token from the header, but I have to save it and for that I need to access the shared preferences, is correct to have access to it inside my interceptor? And in every request I had to send it to my backend, what's the best approach ?
I also saw this tutorial https://medium.com/#tsaha.cse/advanced-retrofit2-part-2-authorization-handling-ea1431cb86be but I think that it's not correct to have access to database inside your application class, am I wrong?
Thank you all for reading this, I'm struggling to find out the best approach, so any help are welcome.
it's not really related to CleanArchitecture. Here're answers for your questions:
I have to save it and for that I need to access the shared preferences, is correct to have access to it inside my interceptor?
-> Yes, you should save it to SharedPreferences, but you should not access SharedPreferences inside your interceptor. You should make your interceptor Singleton and create a new setHeaderToken(String token) function in your interceptor. After authorizing, you can set header token to your interceptor. Something like:
class MyInterceptor{
String token = null;
public void setHeaderToken(String token){
//do set...
};
#Override
public Response intercept(Chain chain) throws IOException {
if(token == null) //do nothing
else // do add header
}
}
// add the singleton Interceptor to your OkHttp Client Builder and use it.
You should not connect Application class with your database directly. You should use a domain layer to do so instead.
I am creating app that consumes REST Api. My API has ability to login/logout in order to access private data. I am creating consumer(client Android app) with Retrofit + Robospice + Jackson.
Everything was okey, but than authorization part came into play.
I need to provide token and other credentials in my request Authorization Header.
This can be easily done by using RequestInterceptor. Here is complete tutorial how to use basic Authentication.
Basic Authentication with Retrofit .
It is clear how to implement this. But in my case I also have resources that can be accessed without credentials.
Here is part of my API declared with Retrofit Annotations
public interface MalibuRestApi {
//Doesn't need credentials
#GET("/store/categories")
Category.List categoryList();
//Doesn't need credentials
#GET("/store/categories/{id}/products")
Product.List productList(#Path("id")int categoryId);
// Needs credentials
#POST("/store/users/{id}/logout")
// Needs credentials
User logout(#Path("id") int id,#Body Credentials userCredentials);
// Needs credentials !!!!!!!!!!!!!!!!!!!!
#POST("/store/users/{id}/orders/")
void makeAnOrder(#Path("id") int userId,#Body Order order,Callback<Void> callback);
}
Please have a look on makeAnOrder method. It uses POST body to pass details about order. So combining credentials and order seems horrible and not efficient, and I won't use it under no circumstances.
It is possible to use interceptor.
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
if
String token = .... // getting token.
request.addHeader("Accept", "application/json");
request.addHeader("Authorization",token);
}
});
I can filter requests and add Auth headers where I need them according to request URL, but ......
According to the discussion here.
#JakeWharton
The relative URL is not exposed in the request interceptor because it
may not be fully resolved yet. A request interceptor has the ability
to perform path replacements and append query parameter
There is one possible workaround.
#powerje
I'm not sure how to access the relative URL in RequestInterceptor but
the solution I used in a similar situation was to check my UserManager
(a global which manages the currently logged in user) to see if a user
is logged in, if they are I add the auth header, otherwise I don't.
I have already similar class Session manager that is created in custom Application class, so I assume that it will live until application(linux dalvik/art process) is destroyed.
So it is possible to do something like this.
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
sessionManager =(SessionProvider) getApplicationContext().getSessionManager();
if(sessionManager.userLoggedIn) {
String token = .... // getting token.
request.addHeader("Accept", "application/json");
request.addHeader("Authorization",token);
}
}
I haven't tested this yet, but it pretends to work.
In this case redundant headers are passed to public resource requests, that don't really require them.
So maybe this can be a sort of solution not a question, but I really need some advice about any other ways (maybe not better) to solve this problem.
I will be grateful for any help.
I'm using ACRA to report exceptions and would like to include the last API call (and potentially the response body) as part of the custom data that can be supplied. I've been looking over the available RestAdapter.Builder interfaces but don't see one that supplies what I'm looking for. Effectively, I'd like access to what's put into Logcat when full logging is enabled.
Is there a way to get the last URL, headers, and response body within Retrofit so I can set custom ACRA fields?
Retrofit allows specifying a Profiler which gets invoked immediately before and after requests are made.
public interface Profiler<T> {
T beforeCall();
void afterCall(RequestInformation info, long elapsedTime, int statusCode, T beforeData);
}
While you don't get access to the actual body, you get a good bit of information about it.
I am making an API call to a web service from the android application the problem is that it returns around 22000 records, I am loading this into an array after i convert each record into an object then assign that Array to a ListView. What is the fastest/best way to fetch this data from the web service? (buffer) ? and what are the best practices for this type of issues.
I would recommend using a library to handle your data call...
Please try using Android Query; specifically, see the section entitled Asynchronous Network.
This AQuery library (AndroidQuery) is lightweight, and requires only 1 jar SMALL jar file. It can be used with Maven or Gradle Android projects as well. It allows you to EASILY fetch XML or JSON data from a remote server in either asynchronous or synchronous fashion. I have used it many times with a JSON back-end, and it is a real timesaver.
This library also allows you to specify a ProgressBar that will automatically appear and disappear during the network download process.
Here is an example of an HTTP call to a JSON back-end, asynchronously:
public void asyncJson(){
//perform a Google search in just a few lines of code
String url = "http://www.google.com/uds/GnewsSearch?q=Obama&v=1.0";
aq.ajax(url, JSONObject.class, this, "jsonCallback");
}
public void jsonCallback(String url, JSONObject json, AjaxStatus status) {
if(json != null) {
//successful ajax call
} else {
//ajax error
}
}
AQuery can also simplify other aspects of Android programming (such as eliminating the findViewById() calls for many scenarios).
I am using Android Annotation in my project and trying to send POST request through following code, however there is something wrong in following code as I am not getting response as expected:
#Rest(rootUrl = "http://xyz.com", converters = {GsonHttpMessageConverter.class})
public interface A {
#Post("/authenticate/email/")
public Object attemptLogin(Map data);
}
Where data is (key, value) pair. Is there anything I am missing perhaps Do I have to set request-header or data should not be JSON?
I found the solution from Rest client using Android-Annotations.
Like the GET requests, it is extremely simple to send POST requests using Android-Annotations. One difference is that you need to define the parameters that you are going to send as a custom class (e.g. Event class in the example below) or if you want to control this dynamically, then a Map (e.g. a MultiValueMap). The url for the request can still be constructed in a similar fashion using the variables enclosed inside {...} and the response can be handled similarly as in GET requests.
#Post("/events")
void addEvent(Event event);
#Post("/events/{id}")
void addEventById(Event event, long id);
#Post("/authenticate/event/")
Object authenticateEventData(MultiValueMap data);