Combines the powers of OkHttp,
Java 8 interfaces,
and Google's Gson to create a thread-safe,
easy-to-use, and easy-to-test Java 8 interface for handling HTTP requests.
This library requires zero configuration, allows for a high degree of customization, and provides:
- Convenience methods to simplify common operations (e.g. normal GET/POST, POSTing a form, etc.)
- Serialization/deserialization support for
Date
,LocalDate
, andLocalDateTime
classes- Multiple formats are attempted during deserialization:
- Unix Epoch
- ISO-8601
- MM/dd/yyyy
- MM-dd-yyyy
- Multiple formats are attempted during deserialization:
The goal was to create an easier-to-use version of OkHttp that yielded prettier code.
- Implement
HttpRequestHandling
on the class you're going to use to make HTTP requests- Override any of the default behavior if necessary (e.g.
getDefaultHeader()
,isNon200ResponseExceptional()
, etc.)
- Override any of the default behavior if necessary (e.g.
- Make a request to your desired endpoint
- For GET requests =
executeGET
- For POST requests =
executePOST
, orexecuteFormPOST
- For GET requests =
- You're done! Optionally you could marshall the resulting JSON into an object or list of objects
- Marshalling JSON =
toJson
,fromJson
, orfromJsonList
- Marshalling JSON =
If the above does not offer the customization you require you can build a Request
object with one of the
following convenience methods:
buildRequestForGET
buildRequestForPOST
This will return you a Request
object you can then pass to executeRequest()
. If you'd like to customize
the request further simply call newBuilder()
on the resulting Request
and customize to your hearts content.
And lastly, if the above is not helpful, you can always build your Request
from scratch via new Request.Builder()
.
I'm a big fan of functional programming and have come to love the functional Try from vavr, so if you prefer to see examples of pretty-okhttp using Vavr click here.
class WebsiteHealthChecker implements HttpRequestHandling {
public Optional<Integer> fetchStatusCodeFromService(String url) {
try {
return Optional.of(
executeFormPOST(url, formData).getStatusCode()
);
} catch(IOException ex) {
// ...
}
return Optional.empty();
}
}
And an associated, yet completely bogus, Spock test illustrating how testing works:
class WebsiteHealthCheckerTest extends Specification {
void "returns empty optional when service returns non-200"() {
given: 'an implementation that always throws HttpClientException'
def websiteChecker = new WebsiteHealthChecker() {
@Override
HttpResponse executeGET(String url) {
throw new HttpClientException("failed, non-200")
}
}
when: 'we fetch the status code for some service URL'
Optional<Integer> result = websiteChecker.fetchStatusCodeFromService("some-url")
then: 'the exception was swallowed and we received an empty optional'
noExceptionsThrown()
!result.isPresent()
...
}
}
class FormUploader implements HttpRequestHandling {
private Integer uploadFormWithRetry(String url, Map<String, ?> formData, int retryCount) {
try {
return executeFormPOST(url, formData).getStatusCode();
} catch(HttpClientException ex) {
// 400 error - we don't normally want to retry on these
} catch(NoResponseException | HttpServerException ex) {
// 500 error or no response - perhaps retrying the request will work
handleRetry();
}
}
}
class UserRetriever implements HttpRequestHandling {
// Customize the default headers to include some authentication
@Override
public Headers getDefaultHeaders() {
return new Headers.Builder()
.addAll(Defaults.jsonHeaders)
.add("Authorization", "...")
.build();
}
public Optional<User> retrieve(String url) {
User user = null;
try {
HttpResponse httpResponse = executeGET(url);
user = fromJson(httpResponse.getBodyOrNull(), User.class);
} catch(HttpClientException ex) {
handle400();
} catch(HttpServerException ex) {
handle500();
} catch(NoResponseException ex) {
handleNetworkError();
}
return Optional.ofNullable(user);
}
static class User {
private String username;
// Map "website" in JSON to this field
@SerializedName("website")
private String url;
// Getters/Setters etc.
}
}
Due to Java's type-erasure we have to use fromJsonList(json, YourObject.class)
if the
JSON we have is a list of objects instead of an object itself.
Accept
=application/json
Content-Type
=application/json
Override getDefaultHeaders()
to change this.
- The default OkHttp configuration is used
Override getHttpClient()
to change this.
- Serialization & de-serialization support added for
Date
,LocalDate
, andLocalDateTime
Override getJsonMarshaller()
to change this.
In your build.gradle
file:
- Under
repositories
- Add
maven { url "https://jitpack.io" }
, making sure it's the last repo declared
- Add
- Under
dependencies
- Add
compile 'com.github.todd-elvers:pretty-okhttp:4.0.0'
- Add