Lesson 2: Expanding Our First Controller

1. Goals

In this lesson, we’re going to continue the work we started previously, by taking our naive controller implementation to the next level.

2. Lesson Notes

The relevant module you need to import when you're starting with this lesson is: m6-expanding-our-first-controller-start

If you want have a look at the fully implemented lesson, as a reference, feel free to import: m6-expanding-our-first-controller-end

2.1. The findOne() Method

Now that our web support is starting to take shape, let’s do some simple CRUD in our application.

Our current findOne implementation in the ProjectController simply returns some random data. However, what it should be doing is using the service layer.

So let’s inject the ProjectService via the constructor:

Now let’s use it in the findOne method.

Notice that we need the id of the Project, since that’s what we’re searching by. So let’s make sure that’s a parameter in the method here.

Next, we'll simply delegate to the service, extract the potential value and return:

The parameter also needs to be mapped to a path component, or path variable, by using the @PathVariable annotation.

Finally, we'll remove the hardcoded "1" in the URL and replace with the id path variable. Now the findOne method will be:

Let’s now run the application.

We'll open up Postman and consume it from the client side by calling: http://localhost:8080/projects/1.

2.2. Error Handling Problems

There are a few things to address here. We’ll, of course, dedicate a full lesson to improving our exception/error handling, so we won’t fix these here. But let’s at least have a quick look at what the problems are.

One is that we’re seeing the raw exception here which is obviously not ideal.

The second issue is the status code. Logically, we’re expecting back a 404 (Not Found) since we’re trying to access a Resource that isn’t found. However, notice what we’re getting a 500 Server Error which is not correct.

Finally, the third point is that, instead of us, proactively checking the result, we’re allowing the exception to be thrown by an internal piece of logic, namely, the Optional. It’s usually better to take control over that.

We’ll deal with all of the issues in a separate, dedicated lesson.

2.3. Write Operation

So far, we've seen a read operation. Let’s now do a write operation as well, as that’s going to involve other aspects of Spring MVC.

We’re going to do a simple save: basically, the creation of a new Project Resource.

First off, let’s define the basic operation with no mappings:

Next, we're going to map this to an HTTP POST using @PostMapping. We'll also map the body of the HTTP request to the Project variable here, using @RequestBody:

We can now switch back to the client and send our POST request with a Project Resource.

Note: In this lesson, we've also updated the entities definition by changing the generation strategy for the ID:

This is because in some lessons we're loading entities into the database automatically during the startup process. With Spring Boot, this is achieved by adding a data.sql file in the resources of the project. In these cases, we have to change the Id-generation strategy for our entities to be compatible with the automatically created entities. You can find more information on this in the Resources section.

3. Resources

- Guide to Spring Controllers

- Spring Web Annotations

- Quick Guide on Loading Initial Data with Spring Boot

- An Overview of Identifiers in Hibernate

LS - Expanding Our First Controller - transcript.pdf
Complete and Continue