Implementing UPDATE operation

In this step, you'll write a method for updating a blog post. By completing this step, you'll learn to map your service with the HTTP PUT (@Put) method, use parameter injection, and convert request body into a Java object using a request object (@RequestObject).

What you need

You need to have the following files obtained from previous steps. You can always download the full version, instead of creating one yourself.

  • Main.java
  • BlogPost.java
  • BlogService.java
  • BlogServiceTest.java

1. Map HTTP method

Let's start mapping the HTTP PUT method with our service method:

  1. Declare a service method, updateBlogPost(), in the class BlogService.
  2. Map this service method with the HTTP PUT method by adding the @Put annotation.
  3. Bind the endpoint /blogs to the method.
BlogService.java
import com.linecorp.armeria.server.annotation.Put;

public final class BlogService {
  ...

  @Put("/blogs")
  public void updateBlogPost(int id, BlogPost blogPost) {
    // Update a blog post
  }
}

2. Handle parameters

For updating a blog post, let's take a blog post ID (id) and new blog post information to update with. For creating a blog post, we've used Armeria's RequestConverter to convert a request body into a Java object. For a change, let's try using @RequestObject to convert a request body.

  1. Take in the ID value as a path parameter by adding /blogs/:id to the @Put annotation.

  2. Inject the path parameter to the service method by annotating the parameter with @Param.

  3. Convert request body into a Java object by annotating the BlogPost parameter with @RequestObject.

    BlogService.java
    import com.linecorp.armeria.server.annotation.Param;
    import com.linecorp.armeria.server.annotation.RequestObject;
    
    public final class BlogService {
      ...
    
      // Instructions 1 to 3
      @Put("/blogs/:id")
      public void updateBlogPost(@Param int id, @RequestObject BlogPost blogPost) {
        // Update a blog post
      }
    }
  4. For conversion, annotate blog post constructor to map JSON object keys to blog post object members. You can find more information on @JsonCreator here.

    BlogPost.java
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public final class BlogPost {
      ...
    
      @JsonCreator
      BlogPost(@JsonProperty("id") int id, @JsonProperty("title") String title,
               @JsonProperty("content") String content) {
        ...
      }
    }

3. Implement service code

In this step, write the code required for service itself. You need to update the information contained in the blogPosts map. In real services, you'll be retrieving and updating the blog post information from and to a database.

To update a blog post, copy the following code inside the updateBlogPost() method.

BlogService.java
@Put("/blogs/:id")
public void updateBlogPost(@Param int id, @RequestObject BlogPost blogPost) {
  BlogPost oldBlogPost = blogPosts.get(id);
  // Check if the given blog post exists
  if (oldBlogPost == null) {
    // Return a Not Found error. See the next section for instructions
  }
  BlogPost newBlogPost = new BlogPost(id, blogPost.getTitle(),
                                      blogPost.getContent(),
                                      oldBlogPost.getCreatedAt(),
                                      blogPost.getCreatedAt());
  blogPosts.put(id, newBlogPost); // Update the info in the map
  ...
}

4. Return response

Two possibilities are available for the response. If the blog post to update exists, we respond to the client that the update has been successful. The other response informs the client that the blog post doesn't exist.

Return error

Let's return an error for the request to update a blog post that doesn't exist.

  1. Replace the return type of the updateBlogPost() method from void to HttpResponse.
  2. Return a response using Armeria's HttpResponse containing HttpStatus.NOT_FOUND.
BlogService.java
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;

public final class BlogService {
  ...

  @Put("/blogs/:id")
  public HttpResponse updateBlogPost(@Param int id, @RequestObject BlogPost blogPost) {
    ...
    if (oldBlogPost == null) {
      return HttpResponse.of(HttpStatus.NOT_FOUND);
    }
    ...
  }
}

Return success

Let's return the information updated when the target blog post exists:

  1. Replace the return type of the updateBlogPost() method from void to HttpResponse.
  2. Return a response using Armeria's HttpResponse containing the updated information of the post.
BlogService.java
import com.linecorp.armeria.common.HttpResponse;

public final class BlogService {
  ...

  @Put("/blogs/:id")
  public HttpResponse updateBlogPost(@Param int id, @RequestObject BlogPost blogPost) {
    ...
    return HttpResponse.ofJson(newBlogPost);
  }
}

5. Test updating a blog post

Let's try updating the content of the first blog post. Add a method like the following.

BlogServiceTest.java
@Test
@Order(4)
void updateBlogPosts() throws JsonProcessingException {
  final WebClient client = WebClient.of(server.httpUri());
  final Map<String, Object> updatedContent = Map.of("id", 0,
                "title", "My first blog",
                "content", "Hello awesome Armeria!");
  final HttpRequest updateBlogPostRequest =
          HttpRequest.builder()
                .put("/blogs/0")
                .content(MediaType.JSON_UTF_8, mapper.writeValueAsString(updatedContent))
                .build();
  client.execute(updateBlogPostRequest).aggregate().join();
  final AggregatedHttpResponse res = client.get("/blogs/0").aggregate().join();
  final Map<String, Object> expected = Map.of("id", 0,
                "title", "My first blog",
                "content", "Hello awesome Armeria!");

  assertThatJson(res.contentUtf8()).whenIgnoringPaths("createdAt", "modifiedAt")
                .isEqualTo(mapper.writeValueAsString(expected));
}

Run all the test cases on your IDE or using Gradle. Check that you see the test is passed.

You can test this also with Armeria's Documentation service. See Using DocService after adding service methods for instructions.

Next step

In this step, we've implemented a method for an UPDATE operation and used Armeria's annotations; @Put, @Param, and @RequestObject.

Next, at Step 7. Implement DELETE, we'll implement a DELETE operation to delete blog posts.