I'm familiar with how to use dynamic URLs with Retrofit2 but having issue sending username & password in the request. The webAPI works by entering a URL, a login prompt appears on the screen for a username & password, after authentication a JSON response is displayed. The interface is defined for a dynamic URL:
#GET
public Call<User> getJSON(#Url String string);
My request is as follows:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
LoginService service = retrofit.create(LoginService.class);
Call<User> call = service.getJSON("https://username:password#api.url.com/");
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, retrofit2.Response<User> response) {
System.out.println("Response status code: " + response.code());
I'm certain the URL is correct as it works in the browser & but I keep getting error the username & password aren't correct?
I/System.out: Response status code: 401
Also, as far as I can tell I can only use #GET rather than #POST because whenever I try #POST the response code is:
I/System.out: Response status code: 405
At first I tried to follow something similar to this post using an encoded flag because it's an example of how to use #PATH & #URL with Retrofit2 but didn't have any success. That's why I tried the username:password# prepend to the URL. Most of the other examples all use the #POST method.
Any feedback or ideas on how I can authenticate? Thanks
Not sure how to do it in retrofit, but you can add it via an OkHttp interceptor --
OkHttpClient client = new OkHttpClient().newBuilder().addNetworkInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
url = url.newBuilder().username("username").password("password").build();
Request newRequest = request.newBuilder().url(url).build();
return chain.proceed(newRequest);
}
}
).build();
be sure to add this client to your retrofit instance --
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Another way to use basic authentication with Retrofit2 would be to pass the authentication string as an argument to your interface method.
So you would change the method signature to:
#GET
public Call<User> getJSON(#Url String string, #Header("Authorization") String myAuthString);
And then call it like this:
Call<User> call = service.getJSON("https://api.url.com/", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
Where you substitute QWxhZGRpbjpvcGVuIHNlc2FtZQ== for your Base64-encoded username:password string.
If you need to pass the username and password for every API call and want to keep the method signatures clean, it might be better to use the custom OkHttpInterceptor method instead.
Related
I have an Api https://hello.example.com:344/new/search/result.
Implementing same using Retrofit 2:
This is how initialising retrofit:
public static void initializeRetrofit() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://hello.example.com:344")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
service2 = retrofit.create(ContentService.class);
}
This is the interface request:
#POST("new/search/result")
Call<JsonObject> getSearchList(#Body JsonObject request);
But when i hit api : it removes the port from it and hits
"https://hello.example.com/new/search/result"
What is going wrong?
In your base url "https://hello.example.com:344" transform it to
"https://hello.example.com:344/"
There is no / (slash) in your base url as well as in the interface function. So the request becomes like "https://hello.example.com:344new/search/result " which will give u an error.
Add slash at the end of your base url like this "https://hello.example.com:344/"
Is there any way to edit the body of a network call for adding a default attribute used in the 95% of the calls?
I've seen that a query parameter is pretty easy to add (link)
But, I have not seen it for a Body.
My problem is that I'm working with an old API that asks me to send in each request the token. So I need to add this line in most of the classes.
#SerializedName("token") val token: String
Any ideas?
You should use httpInterceptor to solve this problem if you send in header
final OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
// add token key on request header
// key will be using access token
.addHeader("token", yourToken)
.build();
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
Edit : Im sorry, I've realized now you ask about sending in body.
I think it can be possible with old way(without Gson, Moshi etc). It is really more annoying than adding to every request.
My app uses dynamic URLs to make web-service calls (on Android). baseUrl is set as empty and we pass Retrofit2 #Url parameters in the service interface:
public interface UserService {
#GET
public Call<ResponseBody> profilePicture(#Url String url);
}
We don't know the host/domain in advance, so MockWebServer is not able to intercept the requests. The call to fetch the initial list of dynamic URLs is made in different screens. One idea is to create a new flavor providing a local data source for URLs to be used, which is my fallback plan.
I am curious if MockWebServer has any other methods to help test such cases and can be limited to test code.
You could use an OkHttp interceptor to rewrite the hostname and port?
I was also facing the same kind of issue. When I use MockWebserver in testing I have to change base URL to target mock web server localhost and port. I tried this it is working fine.
private static final Interceptor mRequestInterceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
final InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
HttpUrl httpUrl = request.url().newBuilder().scheme("http://").host(address.getHostName()).port(8080)
.build();
request = request.newBuilder()
.url(httpUrl)
.build();
return chain.proceed(request);
}
};
After this base url changes to "http://localhost:8080/"
I am trying to compute a checksum from HTTP arguments dynamically. And then I would like to add this checksum as an HTTP argument.
I need to get the fields that are passed in as parameters first, but it looks like retrofit can only access url query parameters.
#Gordak shows the way to get query parameter, but what I want to achive, if any possible, to get post parameters in the request chain.
Okay, here we go.
First, build your OkHTTP client and retrofit object.
OkHttpClient client = httpBuilder
.addNetworkInterceptor(INTERCEPTOR_REQUEST_ADD_CHECKSUM)
.build();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl("https://my.domain.com")
.build();
Then, you need to define your interceptor :
private static final Interceptor INTERCEPTOR_REQUEST_ADD_CHECKSUM = new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
HttpUrl url = chain.request().url();
String param1 = url.queryParameter("param1");
String param2 = url.queryParameter("param2");
String chk = aMethodToComputeChecksum(param1,param2);
url = url.newBuilder().addQueryParameter("checksum", chk).build();
Request request = chain.request().newBuilder().url(url).build();
return chain.proceed(request);
}
Maybe it will help - try to compute this parameter once and write it in RequestInterceptor
I am developing Android App interacting with Twitter using Fabric and Retrofit2 libraries. I want to display search timeline. My request URL is like this: https://api.twitter.com/1.1/friends/list.json?screen_name=xxx
The response body I got is null but I got the alert of bad authentication:215 and http error 400 in the debug mode.This is probably caused by invalid authentication of the request from my app.
The Twitter developer document said requests need to be authorized with OAuth and SSL certificated.
As for the OAuth issue, I wrote the request header based on the official document of twitter developer platform https://dev.twitter.com/oauth/overview/authorizing-requests
and create the header with okhttpclient and pass it to retrofit object.
The code for OAuth issue is like this.
public class TwitterClientApiClient extends TwitterApiClient {
private static final String TAG=TwitterClientApiClient.class.getSimpleName();
private static final MainApplication app=MainApplication.getInstance();
public static final String BASE_URL = "https://api.twitter.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
final String authStr = app.authStr();
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization", authStr)
.method(original.method(), original.body())
.build();
Headers okHeaders = request.headers();
Log.d(TAG,okHeaders.toString());
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
return retrofit;
}
public TwitterClientApiClient(TwitterSession session) {
super(session);
}
public FriendsService getFriendsService() {return getService(FriendsService.class);}
}
interface FriendsService {
#GET("/1.1/friends/list.json")
Call<FriendsResult> list(#Query("screen_name") String screen_name);
}
The following is the code making the request.
FriendsService apiService =
TwitterClientApiClient.getClient().create(FriendsService.class);
Call<FriendsResult> call = apiService.list(screenName);
Log.d(TAG, call.request().url().toString());
call.enqueue(new Callback<FriendsResult>() {
#Override
public void onResponse(Call<FriendsResult> call, Response<FriendsResult> response) {
//List<User> friends = response.body().getUsers();
Log.d(TAG,response.body().toString());
//Log.d(TAG, "Number of Friends: " + friends.size());
//String q = getQueryStr(friends);
//showSearchedTimeline(q);
}
#Override
public void onFailure(Call<FriendsResult>call, Throwable t) {
Log.e(TAG, t.toString());
}
});
However,according to https://oauth.net/core/1.0/#encoding_parameters
OAuth Authentication is done in three steps:
1.The Consumer obtains an unauthorized Request Token.
2.The User authorizes the Request Token.
3.The Consumer exchanges the Request Token for an Access Token.
My code which is based on references from the internet seems to do only Step 3 and thus the authentication is not complete. I wonder how to complete the whole authentication process of OAuth.
Also do I need to do sth in my code for SSL stuff?
Besides OAuth and SSL, any other security issue for request to twitter server I have overlooked?
Thanks in advance!
.header("Authorization", authStr)
Try with addHeader. You can activate the logs (useful to debug sometimes) using a logging interceptor. Ask the logger to show your headers, to see if that could be the problem. Available levels are here.