I have a retrofit api interface where one of the calls requires OAuth1 authentication and the others do not. I am trying to find a way to add the Authentication header without using an Interceptor for a few reasons.
At the time the OkHttp Client is created I wont know OAuth1 keys and the client is used for other things too.
It is only one request that needs OAuth1 authentication
What I was trying to do is wrap the interface call in a Call and then adding the header before I process
val call = api.getRecentTweetsV1("$query -filter:replies -filter:retweets")
val oauth = Oauth1Signing.Builder()
.consumerKey(consumerKey)
.consumerSecret(consumerSecret)
.accessToken(token)
.accessSecret(tokenSecret).build()
val authHeader = oauth.signRequest(call.request())
call.request().headers.newBuilder().add("Authorization", authHeader).build()
val headers = call.request().headers
response = call.execute().body()
The problem is I don't see the header getting added in, the call.request().headers value is always empty after I try to add it in.
I cant use the #Header parameter in the interface call because the OAuth1Signer (modified version of this, I removed the Interceptor usage) takes in a OkHttp Request object to build the header.
Is there another way to add the header after the request has been built?
Related
I have an application that is implemented with clean architecture with MVVM pattern. In this app we need a refresh token request that is needed in all the app features. When a refresh token is success then call the last request again. What is the best way to implement this?
I have two idea:
1 - Implement it in every feature and use it. So if I have three features in my app I will implement it three time.
2 - Implemented globally
I know the first idea but I can't figure out how to do the second one which I think is better.
I use retrofit for networking . The structure is : data , domain , presentation .
With Retrofit you can create a custom Authenticator which will trigger when a request was denied because of an authentication error (typically 401, the documentation for the Authenticator interface explains more). In your authenticator you can retrieve a new token and automatically create a new request with the new token.
An authenticator will be something like:
class Authenticator : okhttp3.Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// Make your network request to retrieve a new token
val newToken = ...
// Check if a new token was retrieved
val retrievedNewToken: Boolean = true
return if (retrievedNewToken) {
response.request().newBuilder()
.header("Authorization", "token-value-here")
.build()
} else {
// Couldn't get new token, abort the request
null
}
}
}
And use it in your OkHttpClient:
val client = OkHttpClient.Builder()
.authenticator(Authenticator())
.build()
This is a fairly general answer as I can't provide any specific way of doing it since I don't know the rest of your code, but that's the gist of it. Something to be aware of is that you might need to handle if a new token request is already happening, as it will potentially make multiple requests for a new token if you make several requests right after each other that all are denied.
I understand that this is how the interceptor works and a request from the application passes through the OkHttp core, via retrofit wrapper and OkHttpp core call to make an actual network request and the network response to the application interceptor response via the retrofit wrapper.
Is there a way to avoid calling the actual request from the application interceptor, as in, in some scenario in application interceptor check if the request URL is some string, then, in that case, do-not-call the actual network request and directly return the hard-coded response from the application interceptor?
You can return a new Response instead of calling chain.proceed() and it would stop the chain from moving forward. You can do it like this.
if(something)
return Response.Builder()
.code(200) //Or whatever you might later check from
.protocol(Protocol.HTTP_2) //or 1
.message("SUCCESS")
.body(ResponseBody.create(MediaType.get("application/json"), "{\"x\": 1}")) // your response
.request(chain.request())
.build()
I also recommend to define an annotation, and get it in your interceptor instead of checking for the URL.
request.tag(Invocation::class.java)?.method()?.getAnnotation(YourAnnotation::class.java)
Retrofit has so called "retrofit-mock", which is designed specifically for your task - mocking:
https://github.com/square/retrofit/tree/master/retrofit-mock
You can try it, maybe you will find it useful.
Example of usage:
https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleMockService.java
You can create 2 implementations of your retrofit service - real and mocked. And provide one of them via DI depending on build flavor or application mode (demo mode or real http session).
I have an authorization api that returns null body but with access token in headers.
I am able to read the okhttp3.Headers object and also get header names as Set using
Headers headers = response.headers(); // response object of type Response<T>
Set<String> headerNames = headers.names();
But in the code the headers object does not show the custom header (access_token) returned as response. However, in postman i can see the custom header as shown below:
access_token -> { "Token":"adklasldalksdalkdask",
"Provider":"ABC" }
I am using interceptors to get the header as shown:
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Can someone suggest how to read the access token as part of the custom header in auth response?
First print the entire response, body, code, message, header(by logging or something else) and try to find a clue from there.
I would recommend you to read the API docs and see the type of request it is asking for.
Use Postman to check which one of the following is working:
1.form-data
2.x-www-form-Urlencoded
3.raw
4.binary
And then accordingly set the annotations in the method declarations in the interface.
eg: in my case it was taking x-www-form-Urlencoded so I had to mention it using
#FormUrlEncoded
#Headers("Content-Type: application/x-www-form-urlencoded")
in the method declaration.
I call a Rest API of salesforce by post method:
url = "https://test-dev-ed.my.salesforce.com/services/apexrest/AccountUsers/"
client = OkHttpClient()
val jsonIn = FormBody.Builder()
.add("email",URLEncoder.encode("dt1#gmail.com", "UTF-8"))
.add("password", URLEncoder.encode("1","UTF-8"))
.build()
request = Request.Builder()
.post(jsonIn)
.header("Authorization", "Bearer "+accesstoken)
.addHeader("Content-Type","application/x-www-form-urlencoded")
.url(url)
.build()
response = client.newCall(request).execute()
This is rest api:
#HttpPost
global static ID createUser(String email, String password) {
AccountUser__c us=new AccountUser__c();
us.Email__c=email;
us.Password__c=password;
us.Status__c=0;
insert us;
return us.Id;
}
But result return is error:
[{"errorCode":"UNSUPPORTED_MEDIA_TYPE","message":"Content-Type header specified in HTTP request is not supported: application/x-www-form-urlencoded"}]
I had try change application/json to application/x-www-form-urlencoded , but still can't resolve.
I try call a Get method, it is ok.
Why Post method occur error [Content-Type header specified in HTTP request is not supported]?
I would like to suggest a better resolution. Retrofit Library
Even though it is not mandatory to use Retrofit, these are few eye catchy aspects which makes it reliable and handy in similar use case of yours.
Why to use Retrofit?
Type-safe REST Adapter that makes common networking tasks easy
For POST operations, retrofit helps in assembling what needed to be submitted. Eg:- Generating URL encoded form.
Takes care of URL manipulation, requesting, loading, caching, threading, synchronization, sync/async calls
Helps to generate URL using type-aware generated code tied to specific REST API
Parsing JSON using GSON
Retrofit is an API adapter wrapped over OkHttp
The problem that you are facing can be resolved using retrofit like this.
public interface APIConfiguration{
#Headers({"Accept:application/json",
"Content-Type:application/x-www-form-urlencoded"})
#FormUrlEncoded
#POST("user/registration")
Observable<DataPojo> registrationAPI(#FieldMap(encoded = true) Map<String, String> params);
}
That's it, with few annotation the library takes care of Form URL
Encoding and related dependencies.
As it is inappropriate to start from corresponding Retrofit dependencies and sample code, you can go through Reference One and Reference Two for more details.
As per my understanding just checkout the difference the content type header "application/x-www-form-urlencoded" is inefficient for sending large quantities of binary data or text containing non-ASCII characters. The content type "multipart/form-data" should be used for submitting forms that contain files, non-ASCII data, and binary data.
The content "multipart/form-data" follows the rules of all multipart MIME data streams.
https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
Also try your http request by setting your content type header as multipart/formdata.
I am using Retrofit/Robospice to make api calls in an app I've built, with a RetrofitGsonSpiceService. All responses are converted into POJOs using a GSON converter, however there is some information I need to retrieve from the response header. I cannot find any means to get the headers (I can only get the headers if the request is unsuccessful because the raw response is sent in the error object!) how can I intercept the response to grab the headers before it is converted?
It took me a few minutes to figure out exactly what #mato was suggesting in his answer. Here's a concrete example of how to replace the OkClient that comes with Retrofit in order to intercept the response headers.
public class InterceptingOkClient extends OkClient
{
public InterceptingOkClient()
{
}
public InterceptingOkClient(OkHttpClient client)
{
super(client);
}
#Override
public Response execute(Request request) throws IOException
{
Response response = super.execute(request);
for (Header header : response.getHeaders())
{
// do something with header
}
return response;
}
}
You then pass an instance of your custom client to the RestAdapter.Builder:
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new InterceptingOkClient())
....
.build();
RoboSpice was designed in a way it doesn't know anything about the HTTP client you end up using in your app. That being said, you should get the response headers from the HTTP client. As Retrofit may use Apache, OkHttp or the default Android HTTP client, you should take a look and see which client you are currently using. Take into account that Retrofit chooses the HTTP client based on certain things (please refer to the Retrofit documentation, or dig into the code, you will find it), unless you manually specify it.
Retrofit defines an interface for clients called Client. If you take a look at the source code, you will see that three classes implement this interface: ApacheClient, OkClient and UrlConnectionClient. Depending on which of them you want to use, extend from one of those, and try to hook into the code that is executed when a response comes back, so that you can get the headers from it.
Once you do that, you have to set your custom Client to Retrofit.