GET Course
Let's create a route to get a specific course given its offering code.
Read Course | |
---|---|
HTTP Method | GET |
API Endpoint | /api/courses/:offeringName |
Request Path Parameter | offeringName |
Request Query Parameter | |
Request Body | |
Response Body | JSON object (note) |
Response Status | 200 |
Notice the API endpoint; to retrieve an item from a collection, it is common to use an endpoint /api/collection/:id
where id
is the primary key of the item you are searching for.
Here are the unit tests for testing this API endpoint:
@Test
public void getCoursesGivenOfferingName() throws UnirestException {
final String OFFERING_NAME = "EN.601.226";
final String URL = BASE_URL + "/api/courses/" + OFFERING_NAME;
HttpResponse<JsonNode> jsonResponse = Unirest.get(URL).asJson();
assertEquals(200, jsonResponse.getStatus());
assertNotEquals(0, jsonResponse.getBody().getArray().length());
}
@Test
public void getCoursesGivenOfferingNameNotInDatabase() throws UnirestException {
final String OFFERING_NAME = "EN.000.999";
final String URL = BASE_URL + "/api/courses/" + OFFERING_NAME;
HttpResponse<JsonNode> jsonResponse = Unirest.get(URL).asJson();
assertEquals(404, jsonResponse.getStatus());
}
Notice the underlying assumption in these tests are that a course with offering name of EN.601.226
exists in the database yet a course with offering code of EN.000.999
does not.
Here are Postman requests corresponding to the above tests:
Finally, here is the route method that must be added to ApiServer.main
:
get("/api/courses/:offeringName", (req, res) -> {
try {
String offeringName = req.params("offeringName");
Course course = courseDao.read(offeringName);
if (course == null) {
throw new ApiError("Resource not found", 404); // Bad request
}
res.type("application/json");
return gson.toJson(course);
} catch (DaoException ex) {
throw new ApiError(ex.getMessage(), 500);
}
});
Notice how the path contains :offeringName
and how I have used req.params
object to get the path parameter.
Run the test (in IntelliJ). At this point the second test must fail! This is caused since we have not implemented a route handler to handle exceptions. Add the following to the ApiServer.main
method, before any other route method:
exception(ApiError.class, (ex, req, res) -> {
// Handle the exception here
Map<String, String> map = Map.of("status", ex.getStatus() + "",
"error", ex.getMessage());
res.body(gson.toJson(map));
res.status(ex.getStatus());
res.type("application/json");
});
This method maps any ApiError
thrown from another route method and maps it to a JSON object which is then attached to the response that goes to the client.
Now if you run the second test (getCoursesGivenOfferingNameNotInDatabase
) again, it will pass.
Notice we always set the response content type to "application/json". We can refactor this process into a special route method; add this to the end of ApiServer.main
method:
after((req, res) -> res.type("application/json"));
Make sure to run the Postman requests too and ensure the response from our server is as expected.