Level 7 - Milestone 4

Milestone 4 - The Presentation Layer

This milestone is dedicated to the presentation package, and adding a Controller class for our application

Before completion of this milestone, students will:

  • Add a Controller class that will eventually be used to access their functionality
  • Add an endpoint to their Controller class
  • Add Swagger annotations to their Controller methods to better document their code

Add a Controller Class

In the presentation package, add another controller class. In the Cheetah class, we added a class called "LocController.java", as this class provided endpoints that accessed data from the Library of Congress. This class will look very similar to the HomeController, but with one notable difference: instead of @Controller this class will be annotated @RestController.

The @RestController is a convenience annotation that simply combines @Controller and @ResponseBody. This @ResponseBody annotation binds the methods return value to the web response body. In this class, we are expecting to return a piece of data with each request, whereas the HomeController simply redirected users to the Swagger UI.

For now, we will have our method return a simple String, just so we can test that it is working. Once the class is created, rerun the application and visit the Swagger page. You should see that Swagger has automatically noticed our new controller class, and displays all of its endpoints on the Swagger UI. You should be able to execute a request to the endpoint via the Swagger UI, and see the canned response with a 200 status code. At this point, this new controller class should look something like this:

package org.jointheleague.level7.cheetah.presentation;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class LocController {

    @GetMapping("/searchLocResults")
    public String getResults(){
        return "Hello, world!";
    }

}
                

Taking a Request Parameter

It is highly likely that your endpoint will need the user to pass-in some sort of information. For example, you may want the user to supply a topic so that you can return books related to that topic. If we were dealing with a simple java method, we could add a parameter and call it a day. While this is actually the first step of the process, we need to add another annotation directly before our method parameter to make this work:

@RequestParam(value="q")


This allows the user to supply the parameter as part of the URL. Not only is this an easy way to accept a value from the user, but is actually a best practice in terms of REST. One of the key principles of REST is that a URL should uniquely identify a resource. In simple terms, if the request was meant to return information on all books ever written, then localhost:5000/searchLocResult would suffice. However, we are intending to return books that relate to a specific topic, and our URL should reflect that.

The name "q" for our URL parameter is a standard choice. In fact, if you make a google search you will find that they use the same URL parameter to represent the query term for the search.

At this point, the above method getResults() method would look like this:
@GetMapping("/searchLocResults")
public String getResults(@RequestParam(value="q") String query)
    return "Searching for books related to " + query;
}


At this point, rerun your application and reload the Swagger UI page. You should see that when you go to execute a request on this endpoint, there is now a field for you to supply the query parameter. You may also notice that once you execute your request, there is a section labeled "Request URL" that shows the exact URL that the request was sent to. You can feel free to plug that directly into your browser, and you should see it working as expected.

Improving the Endpoint Documentation

Before we move on for now, lets improve the documentation for this endpoint. While Swagger is great and taking care of documenting things for us, we can provide addition clarification. First, lets add an @ApiOperation annotation that will allow us to describe this endpoint, shown below in the context of the method:

@GetMapping("/searchLocResults")
@ApiOperation(value = "Searches for books matching the search term",
            notes = "Response may include multiple Result values.",
            response = String.class)
public String getResults(@RequestParam(value="q") String query)
    return "Searching for books related to " + query;
}


If you rerun the application and reload your Swagger page, you should see this additional information supplied. Remember that this doesn't change how the code works, it simply allows us to better document our code.

We can also make another improvement, the benefit of which may not be immediately apparent. Ultimately, if there are no results for the search, it would be best if we didn't just return an empty list of results, but instead provided an response status that indicated the search was unsuccessful. While we don't currently have the code to support that, we can go ahead and add an @ApiResponses annotations that allows us to document what users can expect to be returned from a request to this endpoint. Again in the context of our method, this annotation would look like this:
@GetMapping("/searchLocResults")
@ApiOperation(value = "Searches for books matching the search term",
            notes = "Response may include multiple Result values.",
            response = String.class)
@ApiResponses(value = {
            @ApiResponse(code = 200, message = "Result(s) found")
})
public String getResults(@RequestParam(value="q") String query)
    return "Searching for books related to " + query;
}


This documents that when no results are found for a certain search term, we will be returning a status code of "404" instead of an empty list. Remember that status codes are the easiest and most succinct way that we can convey information to the user, and that we should do our best to return meaningful status codes. We will implement the code that will actually return the 404 response code instead of an empty list in a later section.

If you rerun the application and reload the Swagger UI, you should now be able to see all of the information supplied by these annotations in Swagger.

Summary of Code Changes for this Milestone

    Cheetah-Search
    • src
      • main
        • java
          • org.jointheleague.level7.cheetah
          • resources
            • application.yml
    • build.gradle