This is another post in our content flash series celebrating the release of Retrofit 2.0. It explains how to add constant, default and logic (which are computed for some input) values to the HTTP request body for POST and PUT requests. Sounds complicated? We promise, it won't be!
Before we jump in, take a quick look at our list of other Retrofit posts:
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
If you haven't worked with Retrofit before, please go back to the beginning and understand how we create services and execute requests. If you already got experience, keep reading!
Adding a Request for a Feedback Function
Let's assume your boss gave you the task to implement a feedback function to your Android app. The feedback function should allow the user to give some text input along with some general device data, which is collected automatically. Your backend developer has already done the work and described the endpoint to you. The endpoint expects the following:
URL: /feedback
Method: POST
Request Body Params (Required):
- osName=[String]
- osVersion=[Integer]
- device=[String]
- message=[String]
- userIsATalker=[boolean]
Response: 204 (Empty Response Body)
The first three values are data about the device the user is sending his feedback from. The fourth one is his actual message. The last one is a flag, which signals if the message is especially long.
Simple Approach
The first attempt might be to transfer the REST API documentation over to our Retrofit service declaration:
@FormUrlEncoded
@POST("/feedback")
Call<ResponseBody> sendFeedbackSimple(
@Field("osName") String osName,
@Field("osVersion") int osVersion,
@Field("device") String device,
@Field("message") String message,
@Field("userIsATalker") Boolean userIsATalker);
Next, you'd hook it up to the onclick method of the submit button of the feedback form. There you'd gather the data, compute the logic value and call the service method with all of the data's values:
private void sendFeedbackFormSimple(@NonNull String message) {
// create the service to make the call, see first Retrofit blog post
FeedbackService taskService = ServiceGenerator.create(FeedbackService.class);
// create flag if message is especially long
boolean userIsATalker = (message.length() > 200);
Call<ResponseBody> call = taskService.sendFeedbackSimple(
"Android",
android.os.Build.VERSION.SDK_INT,
Build.MODEL,
message,
userIsATalker
);
call.enqueue(new Callback<ResponseBody>() {
...
});
}
That's it! The programmer in you is satisfied with the result, but you can hear your software engineering professor scream in agony. As you can see above in the method declaration of sendFeedbackFormSimple()
, the only true variable is the message of the user. The osName
variable will not change ever and is actually a constant (the Android app will always have Android
as operating system). Next, the osVersion
and device
are default strings depending on the device model. The userIsATalker
is a logic value, which is calculated the same way every time.
In this case, it's not a big issue, since you only have one feedback form in your app. But what if this would be an endpoint you call from multiple places in your app (for example updating the user profile)? Then you'd have a bunch of duplicated code. Of course, you could introduce some constants and a helper method, which keeps the logic in one place, but there is a cleaner way. In the next section we'll show you how you can create a POST/PUT request with just the one true parameter, while the rest gets set automatically.
Advanced Approach With Passing the Only True Variable
First, we've to change the service declaration. The request now is made with a Java object (Retrofit will automatically serialize it):
@POST("/feedback")
Call<ResponseBody> sendFeedbackConstant(@Body UserFeedback feedbackObject);
The parameter is an instance of the UserFeedback
class, which we haven't shown you yet. It's where the magic happens:
public class UserFeedback {
private String osName = "Android";
private int osVersion = android.os.Build.VERSION.SDK_INT;
private String device = Build.MODEL;
private String message;
private boolean userIsATalker;
public UserFeedback(String message) {
this.message = message;
this.userIsATalker = (message.length() > 200);
}
// getters & setters
// ...
}
The constructor for this class only contains the true variable message
, everything else gets set or computed automatically! Since we're passing the entire object, all the values will be sent.
This makes the request call back to our UI code much leaner as well:
private void sendFeedbackFormAdvanced(@NonNull String message) {
FeedbackService taskService = ServiceGenerator.create(FeedbackService.class);
Call<ResponseBody> call = taskService.sendFeedbackConstant(new UserFeedback(message));
call.enqueue(new Callback<ResponseBody>() {
...
});
}
Even if the app has multiple locations where this request is made, all the constant, default and logic values are computed in one single place. The request calls only pass the single variable. This makes your code much more robust against future changes (and easier to read)!
Feel free to send us your questions via Twitter: @futurestud_io or in the comment section below.