Implementing CREATE operation
Table of contents
In this step, you'll write a service method for creating a blog post. By completing this step, you'll learn to map your service with the HTTP POST (@Post
) method and make your own request converter (@RequestConverter
).
What you need
You must have the following files ready for creating a blog post. You can always download the full version, instead of creating one yourself.
- Main.java from Step 1. Create a server
- BlogPost.java from Step 2. Prepare a data object
- BlogService.java from Step 3. Add services to a server
1. Map HTTP method
Let's start mapping the HTTP POST method with our service method:
- Declare a service method,
createBlogPost()
, in the classBlogService
. - Map this service method with the HTTP POST method by adding the
@Post
annotation. - Bind the endpoint
/blogs
to the method.
import com.linecorp.armeria.server.annotation.Post;
public final class BlogService {
...
@Post("/blogs")
public void createBlogPost(BlogPost blogPost) {}
}
2. Handle parameters
Let's receive blog post information through a request body. Armeria's request converter converts request parameters in HTTP messages into Java objects for you. In the request converter, we define what keys of a JSON object to map with what properties of a Java object.
Let's first write a request converter and then register the request converter to the service method.
Write a request converter
Armeria's request converter converts a request body from a client into a Java object for you.
We can use Armeria's default JacksonRequestConverterFunction
as is, but here let's give a go at customizing a request converter for our blog post requests. We want to convert blog post details into a Java object.
Create a BlogPostRequestConverter.java file and declare a class, implementing the
RequestConverterFunction
interface. For the sake of simplicity, generate impromptu IDs for this tutorial.BlogRequestConverter.javapackage example.armeria.server.blog; import com.fasterxml.jackson.databind.ObjectMapper; import com.linecorp.armeria.server.annotation.RequestConverterFunction; import java.util.concurrent.atomic.AtomicInteger; final class BlogPostRequestConverter implements RequestConverterFunction { private static final ObjectMapper mapper = new ObjectMapper(); private AtomicInteger idGenerator = new AtomicInteger(); // Blog post ID }
Add a method retrieving a value of a given key in a JSON object:
BlogRequestConverter.javaimport com.fasterxml.jackson.databind.JsonNode; final class BlogPostRequestConverter implements RequestConverterFunction { ... static String stringValue(JsonNode jsonNode, String field) { JsonNode value = jsonNode.get(field); if (value == null) { throw new IllegalArgumentException(field + " is missing!"); } return value.textValue(); } }
Customize the default
convertRequest()
method as follows.BlogRequestConverter.javaimport com.linecorp.armeria.server.ServiceRequestContext; import com.linecorp.armeria.common.AggregatedHttpRequest; import com.linecorp.armeria.common.annotation.Nullable; import java.lang.reflect.ParameterizedType; final class BlogPostRequestConverter implements RequestConverterFunction { ... @Override public Object convertRequest(ServiceRequestContext ctx, AggregatedHttpRequest request, Class<?> expectedResultType, @Nullable ParameterizedType expectedParameterizedResultType) throws Exception { if (expectedResultType == BlogPost.class) { JsonNode jsonNode = mapper.readTree(request.contentUtf8()); int id = idGenerator.getAndIncrement(); String title = stringValue(jsonNode, "title"); String content = stringValue(jsonNode, "content"); return new BlogPost(id, title, content); // Create an instance of BlogPost object } return RequestConverterFunction.fallthrough(); } ... }
Register a request converter
In this step, assign the request converter we customized to our service method. Annotate the service method with @RequestConverter
and specify the RequestConverterFunction
class as BlogPostRequestConverter.class
.
import com.linecorp.armeria.server.annotation.RequestConverter;
public final class BlogService {
...
@Post("/blogs")
@RequestConverter(BlogPostRequestConverter.class)
public void createBlogPost(BlogPost blogPost) {
// Implement blog service
}
}
3. Implement service code
When the request for creation is received, our request converter creates an instance of a blog post object for us. We want to save the blog post object in the map (blogPosts
) created in the BlogService
class.
Let's store the blog post information in the map by adding line 4, in the createBlogPost()
method.
1@Post("/blogs")
2@RequestConverter(BlogPostRequestConverter.class)
3public void createBlogPost(BlogPost blogPost) {
4 blogPosts.put(blogPost.getId(), blogPost);
5}
4. Return response
Now, it's time to return a response to our client. As the response, return the information received, with additional information including the ID of the post, created time, plus the modified time which would be identical to the created time.
Let's return a response for blog post creation:
Replace the return type of the
createBlogPost()
method fromvoid
toHttpResponse
.Create and return an HTTP response using Armeria's
HttpResponse
with the information of the post created.BlogService.javaimport com.linecorp.armeria.common.HttpResponse; public final class BlogService { ... public HttpResponse createBlogPost(BlogPost blogPost) { ... return HttpResponse.ofJson(blogPost); } }
5. Test creating a blog post
Run the server like we did in Step 1. Create a server by running the
main()
method or using Gradle. When you see the message, "Server has been started", you can try testing the service method.Call the service method for creating a blog post. Here, we'll use cURL.
$ curl --request POST 'localhost:8080/blogs' \ -H 'Content-Type: application/json' \ -d '{"title":"My first blog", "content":"Hello Armeria!"}'
Check the return value. The response includes created and modified times.
{"id":0,"title":"My first blog","content":"Hello Armeria!","createdAt":...,"modifiedAt":...}
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 written a method to implement a CREATE operation and used Armeria's annotations; @Post
and @RequestConverter
.
Next, at Step 5. Implement READ, we'll implement a READ operation to read a single post and also multiple posts.