Lesson 2: Expanding Our First Controller
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.
- Quick Guide on Loading Initial Data with Spring Boot
- An Overview of Identifiers in Hibernate