I am using Retrofit to Post form data and recieve back XML. What I have so far works fine but i want to make some changes. Here is my existing code (and it works):
Here is my interface
public interface SignupUser
{
#FormUrlEncoded
#POST("/createaccount.cfm")
SignupServerResponse signup(#Field("e") String email, #Field("p") String password);
}
Here is the code to call the api (again, this works fine, I will explain below what I want to change)
SignUpDetails mDeets; // this gets initialize and set somewhere else
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint("http://myurl.com")
.setConverter(new SimpleXMLConverter()).build(); // the response is xml, that works fine
SignupUser service = restAdapter.create(SignupUser.class);
SignupServerResponse res = service.signup(mDeets.getE(), mDeets.getP());
How can I make it so that I can pass the SignUpDetails object straight to the signup() method instead of passing in separate Strings? When I change the constructor of signup() to accept SignUpdetails object (see below) and pass my SignUpDetails object in, I get an error saying
No Retrofit Annotation Found
Here is how I would like to define the interface
public interface SignupUser
{
#FormUrlEncoded
#POST("/createaccount.cfm")
SignupServerResponse signup(SignUpDetails deets);
}
And call it like this (instead of passing in all those parameters)
SignupServerResponse res = service.signup(mDeets);
I tried adding #Field above each of my variables in the SignUpDetails class and that doesnt work either (compilation error)
On your interface use #Body annotation for the deets parameter:
#POST("/createaccount.cfm")
SignupServerResponse signup(#Body SignUpDetails deets);
That should convert SignUpDetails into XML (since you use xml converter on your adapter).
It is then your responsibility to parse the XML request body on server.
Related
In my andorid app I am making a GET request using Retrofit2:
http://myapi.com/items/list
But I would also like to make another request e.g.
http://myapi.com/items/list/filter/active:true,min_price:100
So the filter parameter is optional. I am trying to do the following:
#GET("items/items/list{filter}")
Observable<ResponseItems> getItems(#Path("filter") String filter);
and calling it like:
service.getItems("")
and:
service.getItems("/filter/active:true,min_price:100")
But it does not work. So I ended up creating two separate service calls, one with filter param and other without. I think that there should be more elegant method though.
So i've seen what you are trying to achieve.
How your api declaration should looks like:
#GET("items/list/{filter}")
Observable<ResponseItems> getItems(#Path(value = "filter", encoded = true) String filter);
and a call service.getItems("") would lead to http://myapi.com/items/list/ be called
a call service.getItems("filter/active:true,min_price:100") would lead to
http://myapi.com/items/list/filter/active:true,min_price:100 be called.
An encoded property in #Path annotation is set because your optional path parameter contains / and retrofit encodes it without that property.
So as i wrote in comments better use two declarations:
#GET("items/list/")
Observable<ResponseItems> getItems();
#GET("items/list/filter/{filter}")
Observable<ResponseItems> getItems(#Path(value = "filter") String filter);
so you may call it like service.getItems("active:true,min_price:100")
In simple word make method over loading. Create two method with same name and different parameter. Check my below source code. (written in kotlin)
#GET("myportal/news/{id}")
fun getNewsList(#Path("id") id: String): Call<NewsEntity>
#GET("myportal/news/{id}/{dateTime}")
fun getNewsList(#Path("id") id: Long, #Path("dateTime") dateTime: String): Call<NewsEntity>
Its better to use HashMap to send optional params in a request, this allows you to send single/multiple optional params in your request also if you send the HashMap empty it will not affect your request.
Your interface will contain code like snippet below
#POST("yourUrl")
#FormUrlEncoded
Call<YourResponse> yourApiRequest(#Header("Authorization") String token,
#Field("mandatoryParam") int mandatoryParam,
#FieldMap Map<String, String> optionalParamMap);
And your API call will be something like the following
private HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("optionalParam1", value1);
hashMap.put("optionalParam2", value2);
ApiClient.getApiClient().getApiInterface().yourApiRequest(token,
mandatoryParam, hashMap)
.enqueue(new Callback<YourResponse>() {
#Override
public void onResponse(Call<YourResponse> call, Response<YourResponse> response) {
}
#Override
public void onFailure(Call<YourResponse> call, Throwable t) {
}
});
In a case where you don't want to send any optional params you can pass an empty hashmap to your request.
I am using retrofit in my android application but my service sometimes return object data type and sometime array datatype. how can i handle this ? i used object in place of data type in android but am not able to use it properly.
Create an Interface,inside the Interface whenever your service returns a List do like this:
public Interface EndPointInterface{
#GET(Constants.URL_GET_PHARMACY_REPORT)
Call<List<PharmacyReport>> getPharmacyReport(#Query(Constants.PATIENT_ID) String patientId);
}
else if your service returns an Object proceed like this:
public Interface EndPointInterface{
#GET(Constants.URL_FETCH_STORE_INFO)
Call<Store> getStoreInfoByBeaconUUID(#Query(Constants.BEACON_UUID) String beaconUUID);
}
I'm writing a library for a REST API. The library will have to hide to the user the internal complexity of the API as much as possible.
I'm relying on retrofit2 configured to use RxJava to wrap the responses and GSon to parse them.
I'm using retrofit for a REST service.
#GET("search")
Observable<SearchResult> search(#Query("q") String query);
#GET("search")
Observable<SearchResult> search(#QueryMap Map<String, String> params);
#GET("search")
Observable<SearchResult> searchNext(#Query("q") String query, #Query("startKey") String startKey);
#GET("search")
Observable<SearchResult> searchNext(#QueryMap Map<String, String> params, #Query("startKey") String startKey);
The search REST service return a JSON
{
"count": 2334,
"nextBatchKey": "SADjdfsahoi023451sadfjlskdfj02134512",
"results": [ .... ]
}
the total count of result
a key to be used to retrieve the next batch of the list
the actual array of results
To obtain the next batch the service has to be called again with the same parameters and the key returned.
For example, suppose doing a search for q=foo and limit=20: the REST API will return the results matching string foo batched in segments for 20 results each. To get the next batch I would need to create another request with the same two parameters, q=foo and limit=20, adding startKey=<the next batch key>.
I want / need to hide from the user of the library these internal mechanism.
Inside my library I initialize retrofit like this:
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient) // added interceptors
.baseUrl(baseUrl) // my base url
.addConverterFactory(GsonConverterFactory.create(gson)) // custom type adapter factory
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
It generate the service for my interface and use Gson to parse the response wrapping it with RxJava Observable.
Then I obtain the service:
MyService service = retrofit.create(MyService.class)
Note that I do not write the code of this service, as retrofit works it generate the service automatically using my interface (MyService) annotations.
From the library user perspective all this is hidden, he will just receive the service and use it like this:
// obtain the service
MyService service = MyLibrarySDK.getService(context);
// perform the search operation
Observable<SearchResult> resultObservable =
service.search(params);
I want the developer using my library to be able to get the next batch like this:
// then later result == the SearchResult
Observable<SearchResult> nextBatch =
result.searchNext();
The searchNext() call takes no parameters. This means the SearchResult object I created in the previous call should internally know which parameters to use when using the method searchNext(Map parameters, String key).
If the user perform the search call with q=foo this information is not received in the response. And I need it.
Since retrofit create the service I don't know how I can intercept the method parameters passed with the call to service.search().
The only idea I had to address this is to use an OkHttp interceptor, place the query parameters map in a ThreadLocal, then use a custom Gson TypeAdapter to inject those parameters into the SearchResult object.
This solution should technically work but I think it's ugly. Furthermore the interceptor would run for any method of the REST API but I need it only for search queries.
Have you got a better / creative / elegant idea?
Don't think you should have have searchNext() on the result but rather on the service.
Try something like below:
MyService.java
public class MyService {
RestClient.GitApiInterface service = RestClient.getClient();
String query;
Observable<GitResult> resultObservable;
public Observable<GitResult> getUsersByName(String query) {
this.query = query;
resultObservable = service.getUsersByName(query, 0).cache();
return resultObservable;
}
public Observable<GitResult> searchNext() {
Observable nextResultObservable = resultObservable
.flatMap(new Func1<GitResult, Observable<?>>() {
#Override
public Observable<?> call(GitResult gitResult) {
return service.getUsersByName(query, gitResult.getNextPage());
}
});
return nextResultObservable;
}
}
Usage
final MyService service = new MyService();
// First load
service.getUsersByName("tom");
// consecutive calls
service.searchNext();
Sample app - https://gitlab.com/wizardkrishna/retrofit2-sample
References
Consuming REST API using Retrofit2
Observable Caching
I am using Retrofit in android and GsonConverterFactory is converter.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://xxxxxxx.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
I want send POST request with body.
public class MasterRequest
{
}
public class User extends MasterRequest
{
#SerializedName("email")
public String email = null;
}
#POST("{path}")
Call<MasterResponse> registerUser(#Path("path") String path, #Body MasterRequest masterRequest);
path is the URL that append with base URL.
When ever I send child class("User") object in parent class reference(MasterRequest), then converter shown empty json; "{}".
But when I send User class object to below registerUser Method, then it working fine.
#POST("{path}")
Call<MasterResponse> registerUser(#Path("path") String path, #Body User user);
How can I send child class object in parent class instance to make request body?
That's how Gson works. The easiest way to serialize polymorphic objects is use RuntimeTypeAdapterFactory. You can find detailed tutorial here. Works great with Retrofit!
I use android annotations to communicate with the server. In one of the api calls I need to send some text data and an image, say, from gallery.
#Post("/items/addItem.php")
String addItem(Protocol protocol);
How do I attach a MultipartForm with an image along with the post request?
Just use the right Spring converter : FormHttpMessageConverter.
However, this converter only accepts MultiValueMap as method parameter. Please have a look at these two issues: #652 and #660.
If you really want to use any object as parameter, you have to implement your own custom FormHttpMessageConverter which will handle that by using reflection.
DayS is right. An as quotation, you must include the FormHttpMessageConverter in your Rest Interface definition inside the converters array:
#Rest(rootUrl = "http://api.yourapp.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, FormHttpMessageConverter.class })
public interface YourAppApiClient {
#Post("/items/addItem.php")
void getCustomerInformation(MultiValueMap formfields);
}
-Totaly Agree with above answers but for use of mappinjacksonhttpmessageconverter you have to add another library so if you dnt want to use it you can use below example
Or you can consider it as another example also :)
#Rest(rootUrl = CommonUtils.BASE_URL, converters = { ByteArrayHttpMessageConverter.class,
FormHttpMessageConverter.class, StringHttpMessageConverter.class })
public interface CustomRest extends RestClientErrorHandling{
#Post(CommonUtils.pUrlLogin)
String _Login(MultiValueMap<String, Object> multiValueMap);
#Post(CommonUtils.pUrlSignUp)
String _SignUp(MultiValueMap<String, Object> multiValueMap);
}