so I'm trying to send a simple String to my REST server from an Android app using androidannotations.
http://localhost:8080/TestServer_RESTJersey/api/lanceurs/parPays
Using Advanced REST client chrome extension, I send the parameter :
country=Europe
and it's working fine. Now my problem whith the Android app is that my request is received by the server, but the country parameter is always null. My others GET requests are all working perfectly.
Here is my RestClient class :
#Rest(converters = {MappingJacksonHttpMessageConverter.class, FormHttpMessageConverter.class})
public interface RestClient extends RestClientRootUrl, RestClientSupport{
#Get("/poke/simple")
public MessageResponse simplePoke();
#Get("/api/lanceurs/{name}")
public LaunchVehicleResponse nameRequest(String name);
//server doesn't get the parameter here...
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(String country);
}
Any help would be appreciated as usual, thanks!
EDIT :
server-side REST api :
#Path("api/lanceurs/parPays")
#POST
public String getLanceurByCountry(#FormParam("country") String country)
{
initData();
LaunchVehicleResponse lvr = new LaunchVehicleResponse();
ArrayList<LaunchVehicle> allv = myDatabase.getDataByCountry(country);
lvr.setData(allv);
return parseObjectToJson(lvr);
}
In JAX-RS, use #QueryParam annotation to inject URI query parameter into Java method. example,#QueryParam("country") String countryName,
Try the below, i guess, it should work
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(#QueryParam("country") String country);
Ok, it seems I figured out a way to get myself out of this mess.
I made a class LaunchVehicleRequest on my client, containing (among other things) a country String. When I need to send a request to my server, I instantiate this class and initialize LaunchVehicleRequest.country with the value I want (ex: "USA"). Then I send the whole object to my RestClient.
LaunchVehicleRequest lvreq = new LaunchVehicleRequest();
lvreq.setCountry("Europe");
LaunchVehicleResponse lvr = pm.countryRequest(lvreq);
...
#Rest(converters = {MappingJacksonHttpMessageConverter.class, FormHttpMessageConverter.class}, interceptors = { LoggingInterceptor.class } )
public interface RestClient extends RestClientRootUrl, RestClientSupport, RestClientHeaders{
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(LaunchVehicleRequest request);
}
I set up the same class on my server-side, which get the request as a string and then convert it in an object.
#Path("api/lanceurs/parPays")
#POST
public String getLanceurByCountry(String request)
{
// request={"country":"USA"}
//my json parsing function here
LaunchVehicleRequest lvreq = parseJsonToRequest(request);
...
}
I don't know is this is the best way, but hey it's working fine now and I'm using my LaunchVehicleRequest class for every different request I can need to, so it's not THAT bad I guess ^^'
Thanks everyone anyway ;)
As explained on the wiki, you can send form parameters this way:
#Rest(rootUrl = "http://company.com/ajax/services", converters = { FormHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class })
public interface MyRestClient extends RestClientHeaders {
#RequiresHeader(HttpHeaders.CONTENT_TYPE)
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(MultiValueMap<String, Object> data);
}
MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
data.set("country, "Europe");
client.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
client.countryRequest(data);
Related
Background
On some API calls to the server, instead of the normal parameters to the interface, like this:
interface SomeInterface {
#POST("first_launch") fun sendFirstLaunch(
#Path("referral_code") referralCode: String?,
#Path("referral_source") referralSource: String?): Call<BaseDCResponse>
}
We actually need to send those parameters as a JSON in the body.
The problem
I'm not an expert in Retrofit, but according to what I've found (here for example), I can only pass a Json String to the interface, meaning:
interface SomeInterface {
#POST("first_launch") fun sendFirstLaunch(#Body jsonString: String): Call<BaseDCResponse>
}
According to here, I believe I can also send a serialized object, instead. Meaning something like:
interface SomeInterface {
class SendFirstLaunchRequest(#Path("referral_code") val referralCode: String?,
#Path("referral_source") val referralSource: String?)
#POST("first_launch") fun sendFirstLaunch(
#Body body: SendFirstLaunchRequest): Call<BaseDCResponse>
}
This loses the nice way to reach the function, while making me add the Json data manually for each function I put on the interface (or create new classes to pass there). I want to avoid this, and have something similar to the original.
What I've tried
I tried to search more and more about this, but it doesn't seem like this was requested.
Maybe I saw the answers but didn't understand them.
I think even the official website shows some clues about this:
https://square.github.io/retrofit/
Seeing that I don't think it's possible, I've also added a request for it here.
The questions
Does Retrofit allow to send the parameters I set to the function, to be a Json data as a body?
If not, is there any nice workaround for this? Am I correct that I could only pass a serialized object instead? If so, what's the proper way to do it? Maybe like here?
Using Retrofit2:
I came across this problem last night migrating from Volley to Retrofit2 (and as OP states, this was built right into Volley with JsonObjectRequest), and although Jake's answer is the correct one for Retrofit1.9, Retrofit2 doesn't have TypedString.
My case required sending a Map<String,Object> that could contain some null values, converted to a JSONObject (that won't fly with #FieldMap, neither does special chars, some get converted), so following #bnorms hint, and as stated by Square:
An object can be specified for use as an HTTP request body with the #Body annotation.
The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.
So this is an option using RequestBody and ResponseBody:
In your interface use #Body with RequestBody
public interface ServiceApi
{
#POST("Your api end point")
Call<ResponseBody> login(#Header("X_API_KEY") String header, #Body RequestBody body);
}
In your calling point create a RequestBody, stating it's MediaType, and using JSONObject to convert your Map to the proper format:
Map<String, Object> jsonParams = new ArrayMap<>();
//put something inside the map, could be null
jsonParams.put("name", some_code);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(new JSONObject(jsonParams)).toString());
//serviceCaller is the interface initialized with retrofit.create...
Call<ResponseBody> response = serviceCaller.login(header, body);
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call,retrofit2.Response<ResponseBody> rawResponse)
{
try
{
//get your response....
Log.d(TAG, "RetroFit2.0 :RetroGetLogin: " + rawResponse.body().string());
}
catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
// other stuff...
}
});
Hope this Helps anyone!
Seems it doesn't, and that there is a request to offer something to help handling with this:
https://github.com/square/retrofit/issues/2890
I am trying to get the CompanyEndpoint for each client's site but I am confused with the use of retrofit on the interface.
Here's what I have so far:
CompanyName : "company1"
CompanyEndpoint : "https://example.com"
IdentityEndpoint : "https://example.com/identity"
AppLoginMode : "Anonymous"
AppRouterApi.java
public interface AppRouterApi {
#GET("api/sites/{CompanyName}")
Call<Company> getCompanyName (#Url String companyName);
}
Company.java
public class Company {
String Endpoint;
public String getEndpoint() {
return endpoint;
}
}
MainActivity.java
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
appRouterApi = retrofit.create(AppRouterApi.class);
getCompany();
}
private void getCompany(){
retrofit2.Call<Company> companyRequest = appRouterApi.getCompanyName(); //Error here saying a string cant be applied to ()
companyRequest.enqueue(new retrofit2.Callback<Company>() {
#Override
public void onResponse(retrofit2.Call<Company> call, retrofit2.Response<Company> response) {
if(!response.isSuccessful()){
textViewResult.setText("Code:" + response.code());
return;
}
Company company = response.body();
String content = "";
content += "Url" + company.getEndpoint();
textViewResult.setText(content);
}
#Override
public void onFailure(retrofit2.Call<Company> call, Throwable t) {
}
});
}
https://example/sites/{companyName}
So if I search for:
https://example/sites/company1
The JSON will have one object and I need to get the endpoint URL value which would be: https://company1.com
Edit: My textViewReslt is returning 403
There are several things going on as far as I can tell. Let me break it into chunks.
First thing is you're confusing the annotation #Path with the annotation #Url. They serve different purposes.
You use #Path when you want to format a bit of the path into the url inside the annotations like #GET.
public interface AppRouterApi {
#GET("api/sites/{CompanyName}")
Call<Company> getCompanyName (#Path("CompanyName") String companyName);
}
This interface will format the argument passed to getCompanyName as part of the path. Calling getCompanyName("foo") will call the endpoint "https://example.com/api/sites/foo".
You use #Url when you want to simply call that url. In this case, you only annotate the interface method with the http method. For example,
public interface AppRouterApi {
#GET
Call<Company> getCompanyName (#Url String url);
}
You then would have to call the method with the entire url. To call the same url as before you'd have to call getCompanyName("https://example.com/api/sites/foo").
This is the main difference of usage between these 2 annotations. The reason why you're seeing null in your text view is because you're model's attribute name doesn't match the json. You have 2 options.
First, you can change the model to:
public class Company {
String CompanyEndpoint;
public String getEndpoint() {
return endpoint;
}
}
CompanyEndpoint is the exact same name as you have in the json. Another approach, is to tell your json serializer what name you want to use. Since you're using gson, you can use #SerializedName like so:
public class Company {
#SerializedName("CompanyEndpoint")
String Endpoint;
public String getEndpoint() {
return endpoint;
}
}
#SerializedName("CompanyEndpoint") tells gson which name to use while serializing and deserializing.
In essence, you have 2 options. You either use the endpoint, or the company's name. If you don't expect the domain to change, I'd suggest using the first approach with the #Path annotation. This is what it's usually done with Retrofit and personally, I think it's easier to handle than passing urls around. My suggestion is, use a model like:
public class Company {
#SerializedName("CompanyName")
String name;
public String getName() {
return name;
}
}
This would let you access the company's name property and call getCompanyName(company.getName()). Retrofit would format the company's name into the path and you'd call the right url.
I have to communicate with the following four RESTServices.
Germany (Default): http://url.com/suggest?query=
Austria http://url.com:82/suggest?query=
Swiss: http://url.com:83/suggest?query=
Spain: http://url.com:84/suggest?query=
Basically I have to call the same RESTService on different TCP-Ports for each Country. When I create a Retrofit-RestAdapter, I have to provide a Endpoint (base-url):
RestAdapter.Builder builder = new RestAdapter.Builder();
ObjectMapper mapper = new ObjectMapper();
builder.setEndpoint("http://url.com");
If I want to access those four RESTServices mentioned above, do I have to create a RestAdapter for each of them? Or is it possible to use only one RestAdapter-instance?
I tried to solve the problem by adding the TCP-Port as part of the RestInterface-annotation, but this does not work:
public interface AutoSuggestRemote {
#GET (":{port}/suggest")
public Response getSuggestions(#Path ("port") Integer httpPort, #Query ("query") String query);
}
I get the following exception in Logcat:
java.lang.IllegalArgumentException: AutoSuggestRemote.getSuggestions: URL path ":{port}/suggest" must start with '/'.
at retrofit.RestMethodInfo.methodError(RestMethodInfo.java:123)
at retrofit.RestMethodInfo.parsePath(RestMethodInfo.java:212)
at retrofit.RestMethodInfo.parseMethodAnnotations(RestMethodInfo.java:165)
at retrofit.RestMethodInfo.init(RestMethodInfo.java:133)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:294)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at $Proxy3.getSuggestions(Native Method)
Therefore my question, if I have to create a RestAdapter-instance for each RESTService, or is there a way to communicat with all four services by using the same RestAdapter-instance.
Retrofit consults the EndPoint class each times it does a request. As previously answered by #JakeWharton in the question Dynamic Paths in Retrofit you could extend the EndPoint class with your own implementation and dynamically set the appropriate port as desired.
Here's the code provided by #JakeWharton modified for your specific purpose.
public final class FooEndpoint implements Endpoint {
private static final String BASE = "http://192.168.1.64:";
private String url;
public void setPort(String port) {
url = BASE + port;
}
#Override public String getName() {
return "default";
}
#Override public String getUrl() {
if (url == null) throw new IllegalStateException("port not set.");
return url;
}
}
You can then use the reference to this FooEndPoint instance to change the port dynamically or once when you initialise.
If you choose to set the port once when initialized then you would simply do this.
FooEndPoint endPoint = new FooEndPoint();
endPoint.setPort(loadPortFromSomeWhere());
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setEndpoint(endPoint);
This will allow you to use a single RestAdapter with multiple ports.
I have a web-service:
#Path("/")
#RequestScoped
public class RegistrationService implements Serializable {
#Inject
private DeviceService deviceService;
#PUT
#Path( "/register/{device}" )
#Consumes( MediaType.TEXT_PLAIN )
#Produces( MediaType.TEXT_PLAIN )
public String device(#PathParam("device") String device) {
this.deviceService.saveNewDevice(device);
return "Succesful!";
}
}
And I have a restlet:
public void sendRegistration() {
ClientResource resource = new ClientResource(REG_URL);
resource.addSegment(ctx.getString(R.string.config_segment_register));
... (?)
}
So the current URL will be something like http:// host:port/application/ws/register/pathParam
How can I do the PUT-call to the web-service? There are methods to add queryParams and I could do addSegment to append the ID to the path, but somehow I need to do the PUT then.
almost there try
resource.put(representation);
where the representation is your plain text document. Probably a StringRepresentation but that is up to you.
Due to unfortunately not getting to work (maybe because of too little knowledge about/understanding of the API and processing) the suggestion by #Caleryn, I made some similar solution to https://stackoverflow.com/a/735090/1343241
#Path("/")
#RequestScoped
public class RegistrationService implements Serializable {
#Inject
private DeviceService deviceService;
#PUT
#Path( "/register" )
#Consumes( MediaType.APPLICATION_FORM_URLENCODED )
#Produces( MediaType.TEXT_PLAIN )
public String device(#FormParam("regId") String regId) {
this.deviceService.saveNewDevice(regId);
return "Succesful!";
}
}
And in the restlet:
Form queryParams = resource.getReference().getQueryAsForm();
queryParams.set("regId", regId);
resource.put(queryParams);
I am new to android developing, my website returns posts with following format in json:
post= {
'artist':'xxxx',
'title':'xxxx',
'text':'xxxx',
'url':'http://xxxx'
}
I know something about receiving a file from the net and saving it to a SD card, but I want to do it on fly, parse it and show in on some text view, can you please give me some simple code for this?
I tried searching but I can't find a good tutorial for this, so this is the last place I'm coming to solve my problem.
A good framework for parsing XML is Google's GSON.
Basically you could deserialize your XML as follows (import statements left out):
public class Post {
private String artist, title, text, url;
public Post() {} // No args constructor.
}
public class Main {
public static void main(String[] args) {
Gson gson = new Gson();
String jsonString = readFromNetwork(); // Read JSON from network...
Post post = gson.fromJson(jsonString, Post.class);
// Use post instance populated with your JSON data.
}
}
Read more in GSON's user guide.