Android: Using Retrofit with dynamic urls - java

I have an url similar to http://www.myexample.com/rss/choose.php?type=level&id=2 and I want to use Retrofit in my android app. The problem is that type is a parameter which can take different values like level, grade, etc.
I can't adapt Retrofit every time type change because I get a "Annotations are not allowed here" in TestesInterface. I was looking to this post and applied to my app but it doesn't work.
static final String BASE_URL = "http://www.myexample.com/rss/";
public void start() {
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(SimpleXmlConverterFactory.create()).build();
TestesInterface testesInterface = retrofit.create(TestesInterface.class);
Call<Channel> call = testesInterface.loadChannel("choose.php?type=level&id=2");
call.enqueue(this);
}
My interface:
public interface TestesInterface {
#GET
Call<Channel> loadChannel(#Url type);
}
This made that every time I want to change type to a different value I should change testesInterface.loadChannel("choose.php?type=level&id=2"). This doesn't work. Can you please help me?

After try different approaches I got the solution.
I had to use the tag #Query to type and id on interface and send the values when I'm invoking it in start():
#GET
Call<Channel> loadChannel(#Query("type") String type,
#Query("id") String value);

Related

How to pass string as a body in api post method using retrofit?

I am developing an android application which sends the user entered data (Through edit textboxes) and it calls a POST API Method to send this data in the body of that API
Consider API URL is "htpps://xxxxx.com/forms/xxxxxx/reponse"
content-type "Json(Application/json)"
content looks like:
{"answers":
"[{\"questionId\":\"r8554145065f6486d8a362bec92030a06\",\"answer1\":\"xxxxx\"},
{\"questionId\":\"rf516c5bf916e4d6d960a1f8abc82f33b\",\"answer1\":\"xxxx\"}]"}
My problem is here how can I pass this type of body to retrofit and instead of "XXXXX" in content there will be a string which takes input from the user?
You probably already are using a interface to make the api calls and thats where you add the String body to the request.
public interface YourService{
#POST("forms/xxxxxx/reponse")
Call<Object> makeCall(#Body String body);
}
If you haven't been using an interface with retrofit than you can create an instance of the above interface by using your existing RetrofitClient:
YourService service = retrofitClient.create(YourService.class);
And now you can access the api by calling makeCall on the instance of the service:
service.makeCall(yourCustomString).enqueue(new Callback<Object>() {
#Override
public void onResponse(Response<MovieResponse> response) {...}
#Override
public void onFailure(Throwable t) {...}
});
You can build "yourCustomString" with String builder or so, even though I wouldnt recommend that, instead I would use an ConverterFactory and map your JSON-Data to Java POJOs. For example Moshi (com.squareup.retrofit2:converter-moshi).
If you do that you can use a Java POJO as the #Body annotated attribute and you simply ony set the two attributes "questionId" and "answer1" on the POJO instead of building a String.
If you would do that, you would end up with two classes:
public class Answer {
#Json(name = "questionId")
public String questionId;
#Json(name = "answer1")
public String answer1;
}
and
public class Body {
#Json(name = "answers")
private List<Answer> answers = new LinkedList<>();
}
Now with that you would just create an Body object and then add as many answers as you want to the answers attribute and then use the Body object as the parameter on the retrofit interface.
Note: If you do that, you have to add the MoshiConverterFactory to the RetrofitClient while building it.
I assume you are already familiar with how to implement Repositories and Interfaces for making API calls.
For the case you first need a DTO for answer.
public class AnswerDTO
{
private String questionId;
private String answer1;
public AnswerDTO(String questionId, String answer1)
{
this.questionId = questionId;
this.answer1 = answer1;
}
}
Now you can create an Interface for API call.
public interface QuestionsService
{
#FormUrlEncoded
#POST("requestUrlHere")
Call<Response> yourApiCall(#Field("answers[]") List<AnswerDTO> answers);
}
Hope this helps !.

java - Cannot get rid of '\' character in JSON once deployed by Lambda/Serverless

I am creating an API (written in Java) which I am deploying through serverless which ports to a AWS Lambda function. All aspects of the API function great except for the fact that the requests which are returned include the '\' character in front of all quotes.
To put this into perspective, I have a person class which contains instance variables for name (String) and mood (String). I then have my class which uses this class to get and create a Person object, and then Jackson is used to parse this into JSON format. This is what is returned to the handler function (for lambda) and is displayed as the "object body".
public class Person{
String name;
String mood;
//getters and setters and constructor
}
Then, later on there will be something in a different class like
Person person = new Person("bob", "good");
Which would be passed into my method which is supposed to convert things to JSON:
private String convStrToJson(Person person) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
return json;
}
If I were to print this in the output, I'd get something like:
{"name":"bob","mood":"good"}
Which is what I want and expect. However, when deployed and called via GET request, the result is:
"{\"name\":\"bob\",\"mood\":\"good\"}"
I've tried several strategies, including additions to the parsing method such as:
json = json.replace("\"", "");
Which removes the quotes fully from both outputs, or:
json = json.replace("\\","");
Which has no effect at all. I also tried both of these as replaceAll methods and that just messed things up even more. I'm not sure what else I can do to get rid of these '\' characters, I understand why they're there but I don't know how to stop that. Any assistance is appreciated.
Okay so I figured it out. Turns out serverless not only includes Jackson, but actually in the layout it creates for handling responses, the "setObjectBody" section will accept any kind of object and use Jackson to parse it to JSON. This is where I messed up. I assumed it would only accept Strings, which is where the double encoding was occurring. Now, if I pass in the Person object, serverless/Jackson handles it appropriately for me and the expected output is returned. I'll include code snippets below to better demonstrate this solution. Serverless creates a 'handler' class which has a template including a method called handleRequest. Once filled in, this class now looks like this:
public class GetStatusHandler implements RequestHandler<Map<String, Object>, ApiGatewayResponse> {
private static final Logger LOG = Logger.getLogger(GetStatusHandler.class);
#SuppressWarnings("unchecked")
public ApiGatewayResponse handleRequest(Map<String, Object> input, Context context) {
BasicConfigurator.configure();
LOG.info("received: " + input);
try {
Map<String, String> pathParameters = (Map<String, String>) input.get("queryStringParameters");
if(pathParameters == null) {
LOG.info("Getting details for all persons ");
PersonControl control = new PersonControl();
Person[] result = control.myGetHandler(context);
return ApiGatewayResponse.builder()
.setStatusCode(200)
.setObjectBody(result)
.setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless"))
.build();
}else {
String name = pathParameters.get("name");
LOG.info("Getting details for "+name);
PersonControl control = new PersonControl();
Person result = control.myGetHandler(name, context);
return ApiGatewayResponse.builder()
.setStatusCode(200)
.setObjectBody(result)
.setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless"))
.build();
}
}catch(Exception e) {
LOG.error(e, e);
Response responseBody = new Response("Failure getting person", null);
return ApiGatewayResponse.builder()
.setStatusCode(500)
.setObjectBody(responseBody)
.setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless"))
.build();
}
}
}
Not that when returning the ApiGatewayResponse (via builder), an object is simply passed in to the .setObjectBody method ('result') which serverless automatically converts to JSON for us. Thats it! No parsing to JSON necessary in the code.
The response can be a user defined object as below
class Handler implements RequestHandler<SQSEvent, CustomObject> {
public CustomObject handleRequest(SQSEvent event, Context context) {
return new CustomObject();
}
}
Sample code can be found here.
Just use the Google Gson java library that can be used to convert Java Objects into their JSON representation.
Gson gson = new Gson();
gson.toJson(person);

how to do a #GET in RXJava in android

I just need to do a #GET for the following endpoint but not sure the syntax, here is my code:
public interface GithubService {
String SERVICE_ENDPOINT = "https://api.github.com";
String SERVICE_FUNDS_ENDPOINT = "http://iwg-testapi.azurewebsites.net";
// this works fine
#GET("/users/{login}")
Observable<Github> getUser(#Path("login") String login);
//here is the problem:
#GET("/stem/funds")
Observable<Funds> getFunds(#Path("funds") String login);
}
It's not a RxJava issue but a Retrofit.
I think the issue is on the GET annotation, as you want to use the path param.
#GET("/stem/{funds}") Observable<Funds> getFunds(#Path("funds")
(Notice that I add {} around funds because I want to use it at the path param)
You may want to check the Retrofit documentation.

Java REST API: Returning Multiple objects from API Method

I am trying to return multiple objects (such as String, Boolean, MyOwnClass, etc) from a Java REST API Method using JAX-RS in Eclipse.
Here's what I have right now:
My API Method
#Path("/")
public class myAPI {
#GET
#Produces({ "application/xml", "application/json" })
#Path("/getusers")
public Response GetAllUsers() {
//Data Type #1 I need to send back to the clients
RestBean result = GetAllUsers();
//Data Type #2 I need to send with in the response
Boolean isRegistered = true;
//The following code line doesn't work. Probably wrong way of doing it
return Response.ok().entity(result, isRegistered).build();
}
}
RestBean class:
public class RestBean {
String status = "";
String description = "";
User user = new User();
//Get Set Methods
}
So I'm basically sending two data types: RestBean and Boolean.
What's the right way of sending back a JSON response with multiple data objects?
Firstly, Java conventions are that class names begin with an uppercase letter and method names with a lowercase letter. It's generally a good idea to follow them.
You need to wrap your response inside a single class, as #Tibrogargan suggests.
public class ComplexResult {
RestBean bean;
Boolean isRegistered;
public ComplexResult(RestBean bean, Boolean isRegistered) {
this.bean = bean;
this.isRegistered = isRegistered;
}
}
and then your resource looks like...
public Response getAllUsers() {
RestBean restBean = GetAllUsers();
Boolean isRegistered = true;
final ComplexResult result = new ComplexResult(bean, isRegistered);
return Response.ok().entity(Entity.json(result)).build();
}
What you really need to know, however, is what your response document should look like. You can only have a single response document - which is what the wrapper is for - and the way your wrapper is serialised affects how the parts of the document are accessed.
Note - you have your resource listed as being able to produce both XML and JSON and what I've done only works for json. You can get the framework to do all the content-negotiation heavy lifting for you, and that would probably be a good idea, by just returning the document type from the method rather than Response ...
public ComplexResponse getAllUsers() {
...
return new ComplexResult(bean, isRegistered);

Declaring a retrofit REST endpoint with constant query value

So I want to get the metadata of a youtube video (say this one: https://www.youtube.com/watch?v=qlTA3rnpgzU).
I'm going to encode it and wrap it in another url like so: http://www.youtube.com/oembed?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqlTA3rnpgzU&format=json
My interface definition will look like this:
public interface YoutubeApi {
#GET ("oembed")
YoutubeMetaData metaData (#Query (QUERY_VIDEO_URL) final String url,
#Query(QUERY_FORMAT) final String alwaysJson);
}
That's all fine and dandy, but I don't ever want to specify any format other than JSON here (format=json is a fixed part of this url).
Is there a way to specify this in my interface declaration and reduce my interface to:
public interface YoutubeApi {
#GET ("oembed")
#Magic ("format=json")
YoutubeMetaData metaData (#Query (QUERY_VIDEO_URL) final String url);
}
Thanks.
Just put it right in the relative URL:
public interface YoutubeApi {
#GET("oembed?format=json")
YoutubeMetaData metaData(#Query(QUERY_VIDEO_URL) String url);
}
In kotlin you can specify the default parameter:
interface YoutubeApi {
#GET ("oembed")
suspend fun metaData (
#Query (QUERY_VIDEO_URL) url: String,
#Query(QUERY_FORMAT) alwaysJson: String = "json"
): Response<YoutubeMetaData>
}

Categories

Resources