Default/Constant values for POST/PUT arguments with Retrofit - java

Using the Retrofit REST Client library from Square, is there anyway of providing default/constant values for POST/PUT fields in a call.
I know about including constant query parameters by simply including them in the path, but this work for Body parameters.
I have an API that looks similar to:
POST /api/create
type=constanta&value={value}
POST /api/create
type=constantb&value={value}&otherValue={otherValue}
where the second variant requires an additional argument to be supplied. Rather than having a single java method that took all three arguments, I was hoping to be able to elide the constants from the method call, something like:
create(String value);
create(String value, String otherValue);
and have retrofit inject the type argument constant.
Given that adding #FormUrlEncoded can be added to modify how the body is encoded, if it's not natively supported by Retrofit, is there anyway of adding my own annotation and injecting such default values? ( It doesn't appear that RequestInterceptor allows one to modify the body.. ).

Maybe one option would be to send an object, which encapsulates all your values, instead of all string values separately? The object would implement your default values.
For example, you could create a class:
public class CreateObject {
private String type = "constant";
private String value;
private String otherValue;
public CreateObject(String value, String otherValue) {
this.value = value;
this.otherValue = otherValue;
}
}
Your class handles your constant. You could just set it to a default value "constant", like I did above, or set it on the fly in the constructor.
Now all you've to do is to create the object with the values and make the request with Retrofit. Instead of using the string values directly, just pass the object. Your interface could look like this:
public interface CreateService {
#POST("/api/create")
void create(#Body CreateObject create, Callback<CreateObject> cb);
}
The request implementation like this:
CreateObject create = new CreateObject("value", "otherValue");
createService.create(create, new Callback<CreateObject)() {…});
This should include all three of your values in the request body, if they are set. If a value is null, it won't be included in the request body. Based on your two examples above, you would now only need one interface method. Which values are sent is based on the createObject you pass on. For example, if you set otherValue as null, it won't be part of the request body.
My examples were modified from: https://futurestud.io/blog/retrofit-send-objects-in-request-body/

Is it possible for you to use Guava or Java 8 Optional as second argument in method? Then if that argument will be absent you can just ignore it

Related

How can I pass a HashMap as parameters

I have a class API, where you set and get the info you need to later be used on a API call
I want to make it easier to the next person who's gonna use this.
So instead of doing this:
api.addURL("urltorequesttoken");
api.addHeader("client_id","sdfsfsdfsd")
.addHeader("client_secret","sdfsdfsfsfd")
.addHeader("grant_type","client_credentials")
.addHeader("scope","READ");
api.addBody("bodyToSend")
I want to do this:
String URL = "";
URL = "put your URL here";
So I pass the URL and other variables as a parameter to another method where I will be doing what I did in the first block of code,so they don't need to know about the API class and its methods, but I dont know how to handle the hashmap, how can I do that user friendly? and then pass that as a parameter, also, what type of parameters should the methods receiving this info have? (Map<String key, String value>) or (String key, String value)?
EDIT(ADD):
So there's a class that a DEV is going to create, let's call it CreateToken
, so that class currently has:
api.addURL("urltorequesttoken");
api.addHeader("client_id","sdfsfsdfsd")
.addHeader("client_secret","sdfsdfsfsfd")
.addHeader("grant_type","client_credentials")
.addHeader("scope","READ");
api.addBody("bodyToSend")
There's another class called BASE, where Im doing the core services, in order for this to be easier for the person when they create their class, I dont want to have that block of code on their class, but instead, on mine, so in their class all they have to do is set the URL, headers and body(for POST method), so instead of this:
api.addURL("urltorequesttoken");
they will do:
URL = "urltorequesttoken";
and there's a method on their class to send me this or for me to get it i,e.
fillAPICallInfo(URL, headers, body);
I will receive that on the BASE class, but I dont know how to handle the Map variables, don't know how to make it easy for the DEV so they just put the key and value, and how do I receive that on my class (as a Map or as Strings)?
So you simply can pass a Map<String, String> as parameter:
public void fillAPICallInfo(String url, Map<String, String> headers, String body) {
// Assuming there is an instance of class DEV named api available
api.addURL(url);
headers.forEach((h, v) -> api.addHeader(h, v));
api.addBody(body);
}

Play framework: how to pass parameters extracted in the onRequest function

I overwrote the public Action onRequest(final Http.Request request, Method method) method to check that the mandatory http headers are passed in and valid, i.e.: I extract the apiKey (and other things) and make sure that these are valid (that there's data associated with the apiKey). Then I call return super.onRequest(request, method); and I end up in my controller where I once again have to extract the apiKey and get the associated data from the DB.
Is there a way to pass in the data to my controller's method (for instance: public static Result addUser() ).
Thank you.
I know this question is for Java, but I work with Play in Scala, and this question is more related to Play then it is to Java
In Scala I am able to extend WrappedRequest to make my own custom request type, that has instance variables I want to access in my controller:
// Scala code
case class MyRequest[A](request: Request[A]) extends WrappedRequest(request) {
// This is a public instance variable
val apiKey = request.headers.get("Authorization)
}
Then later on in my controller I can access the action object which now has the type of MyRequest:
// Scala code
def foobar(action: MyRequest[AnyContent]) = Action {
// Do something with the api key
val apiKey = action.apiKey
// Send back a response
Ok("foobar")
}
In Java it looks like you can do something similar using a Wrapped Context

Override HttpMessageConverter for Spring RequestBody

I have the following code:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Void> flagDevice (#RequestBody List<MobileDeviceData> devicedataList, #RequestHeader(value="special_code") String specialCode) {
// Implementation details
}
Each instance of MobileDeviceData that gets created needs to have a param field filled in with the RequestHeader special_code.
How would I go about doing this so that it is fully populated by the time the flagDevice method body gets called?
Thanks in advance.
This is non trivial.
An HttpMessageConverter is already provided that deserializes the JSON, that's the MappingJackson2HttpMessageConverter. It has access to request headers. You could extend that class to also use the headers for deserialization (this is extremely difficult to do generically, as opposed to only for MobileDeviceData).
You could use Spring AOP, intercept the method, retrieve the arguments, cast to the appropriate types, and assign the value yourself.
The solution I would go for is the simplest: do it yourself in the handler method. Loop the the List and use a corresponding setter to set the specialCode for each MobileDeviceData.
Another option is to define your own HandlerMethodArgumentResolver specifically for List<MobileDeviceData> parameters that need to be constructed from header vales.

Build Liferay web service with optional parameter

I need to extend an existing Liferay webservice (created with Service Builder) to handle an additional optional parameter.
With Service Builder, you have to specify every parameters inside the method signature:
public String getList(String param1){ .. }
This creates a get-list web service accepting a parameter named param1. You have to specify every parameters when making the call or else the call will fail. If you need optional parameters, just pass an empty value and handle the missing parameter inside the code.
My problem is backward compatibility: this web service is already used by a mobile app and I cannot change the call made by the app. The additional parameter must be handled without changing the method signature.
Taking a look at BaseServiceImpl, I tried to obtain the parameter in this way:
HttpServletRequest request = com.liferay.util.axis.ServletUtil.getRequest();
String value = ParamUtil.getString(request, "param-name");
But it throws a NoClassDefException regarding com.liferay.util.axis.ServletUtil.
Is there a way to actually do this?
To enhance and retain backward compatibility of your code, one way is to overload getList() method which accepts additional parameter.
You can achieve this by following:
Move your general pre-logic code of getList() to getList(String param1) method.
Add filter for param1 in getList(String param1) to handle case when parameter is not null / empty.
Call getList(null) from getList().
While you can call getList(String param1) directly when you require to pass additional parameter.
Original method:
public String getList(){
return getList(null);
}
Overriden method:
public String getList(String param1){
if(param1 != null){
// logic for param1
}
// rest of your general code
}

Is it possible to configure JAX-RS method with variable number of URI parameters?

is it possible to configure GET method to read variable number of URI parameters and interpret them either as variable argument (array) or collection? I know query parameters can be read as list/set but I can't go for them in my case.
E.g.:
#GET
#Produces("text/xml")
#Path("list/{taskId}")
public String getTaskCheckLists(#PathParam("taskId") int... taskId) {
return Arrays.toString(taskId);
}
Thanks in advance
If I understand your question correctly, the #Path annotation can take a regular expression to specify a list of path components. For example, something like:
#GET
#Path("/list/{taskid:.+}")
public String getTaskCheckLists(#PathParam("taskid") List<PathSegment> taskIdList) {
......
}
There's a more extensive example here.
I am not submitting this as an answer as it is merely an edge case on the currently accepted answer which is what I've also used.
In my case (Jersey 1.19) /list/{taskid:.+} would not work for the edge case of zero variable parameters. Changing the RegEx to /list/{taskid:.*} took care of that. See also this article (which seems to be applicable).
Moreover, upon changing the regexp to cardinality indicator to * (instead of +) I also had to deal programmatically with the case of empty strings as I would translate the List<PathSegment> into a List<String> (to pass it into my DB-access code).
The reason I am translating from PathSegment to String is that I didn't want a class from the javax.ws.rs.core package to pollute my Data Access Layer code.
Here's a complete example:
#Path("/listDirs/{dirs:.*}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listDirs(#PathParam("dirs") List<PathSegment> pathSegments) {
List<String> dirs = new ArrayList<>();
for (PathSegment pathSegment: pathSegments) {
String path = pathSegment.getPath();
if ((path!=null) && (!path.trim().equals("")))
dirs.add(pathSegment.getPath());
}
List<String> valueFromDB = db.doSomeQuery(dirs);
// construct JSON response object ...
}

Categories