Implementing DELETE operation
Table of contents
In this step, we'll write a method for deleting a blog post. By completing this step, you'll learn to map your service with the HTTP DELETE (@Delete
) method, customize an exception handler, and use a blocking task executor.
What you need
You must have the following files ready for deleting 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 DELETE method with our service method:
- Declare a service method,
deleteBlogPost()
in the classBlogService
. - Map this service method with the HTTP DELETE method by adding the
@Delete
annotation. - Bind the endpoint
/blogs
to the method.
import com.linecorp.armeria.server.annotation.Delete;
public final class BlogService {
...
@Delete("/blogs")
public void deleteBlogPost(int id) {
// Delete a blog post
}
}
2. Handle parameters
Let's take the blog post ID (id
) as a path parameter for identifying the post to delete.
Take in the ID value as a path parameter by adding
/blogs/:id
to the@Delete
annotation.Inject the path parameter to the service method by annotating the parameter with
@Param
.BlogService.javaimport com.linecorp.armeria.server.annotation.Param; public final class BlogService { ... @Delete("/blogs/:id") public void deleteBlogPost(@Param int id) { // Delete a blog post } }
3. Implement service code
In this step, write the code to delete a blog post, handle an exception, and block the operation.
Delete a blog post
Deleting a given blog post in this tutorial means removing a blog post from the map, blogPosts
. However, in real services you would be performing this action on a database.
To delete a blog post, copy line 3 into the deleteBlogPost()
method.
1@Delete("/blogs/:id")
2public void deleteBlogPost(@Param int id) {
3 BlogPost removed = blogPosts.remove(id);
4}
Handle exceptions
What if there is no such post to delete? We can check if the blog exists before attempting to remove the blog post. Here, let's handle it after the attempt.
Throw an
IllegalArgumentException
if no blog post exists with a given ID.BlogService.java@Delete("/blogs/:id") public void deleteBlogPost(@Param int id) { ... if (removed == null) { throw new IllegalArgumentException("The blog post does not exist. id: " + id); } }
Create an exception handler for the blog service:
- Create a file, BadRequestExceptionHandler.java.
- In the file, declare a custom exception handler implementing Armeria's
ExceptionHandlerFunction
interface.
BadRequestExceptionHandler.javapackage example.armeria.server.blog; import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction; import com.fasterxml.jackson.databind.ObjectMapper; public class BadRequestExceptionHandler implements ExceptionHandlerFunction { private static final ObjectMapper mapper = new ObjectMapper(); }
Implement your own exception handler by overriding the default
handleException()
method. Add a code block for handling theIllegalArgumentException
thrown. For this tutorial, return a BAD REQUEST as the response.BadRequesExceptionHandler.javaimport com.fasterxml.jackson.databind.node.ObjectNode; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.HttpStatus; import com.linecorp.armeria.server.ServiceRequestContext; public class BadRequestExceptionHandler implements ExceptionHandlerFunction { ... @Override public HttpResponse handleException(ServiceRequestContext ctx, HttpRequest req, Throwable cause) { if (cause instanceof IllegalArgumentException) { String message = cause.getMessage(); ObjectNode objectNode = mapper.createObjectNode(); objectNode.put("error", message); return HttpResponse.ofJson(HttpStatus.BAD_REQUEST, objectNode); } return ExceptionHandlerFunction.fallthrough(); } }
Assign the exception handler to the
deleteBlogPost()
method by annotating thedeletePost()
method with the@ExceptionHandler
as follows.BlogService.javaimport com.linecorp.armeria.server.annotation.ExceptionHandler; @Delete("/blogs/:id") @ExceptionHandler(BadRequestExceptionHandler.class) public void deleteBlogPost(@Param int id) { ... }
Add blocking
With real services, accessing and operating on a database takes time. We need to hand over such blocking tasks to blocking task executor so that the EventLoop isn't blocked. There are a few options to implement this; we'll annotate our service method with the @Blocking
.
import com.linecorp.armeria.server.annotation.Blocking;
public final class BlogService {
...
@Blocking
@Delete("/blogs/:id")
@ExceptionHandler(BadRequestExceptionHandler.class)
public void deleteBlogPost(@Param int id) { ... }
}
4. Return response
We've already handled returning the not found error in the exception handling section. Here, we'll return a response for successful deletion.
- Replace the return type of the
deleteBlogPost()
method fromvoid
toHttpResponse
. - Return a response using Armeria's
HttpResponse
, containingHttpStatus.NO_CONTENT
.
import com.linecorp.armeria.common.HttpResponse;
public final class BlogService {
...
@Blocking
@Delete("/blogs/:id")
@ExceptionHandler(BadRequestExceptionHandler.class)
public HttpResponse deleteBlogPost(@Param int id) {
...
return HttpResponse.of(HttpStatus.NO_CONTENT);
}
}
5. Test deleting a blog post
Let's test deleting 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 service methods.Create a couple of blog posts to test deleting a blog post and get the ID value returned. Enter the cURL commands below.
$ curl --request POST 'localhost:8080/blogs' \ -H 'Content-Type: application/json' \ -d '{"title":"First post for testing", "content":"Test deletion."}'
$ curl --request POST 'localhost:8080/blogs' \ -H 'Content-Type: application/json' \ -d '{"title":"Second post for testing", "content":"Test deleting a post."}'
For each command, you'll get a response similar to the following.
{"id":0,"title":"First post for testing","content":"Test deletion.","createdAt":...,"modifiedAt":...} {"id":1,"title":"Second post for testing","content":"Test deleting a post.","createdAt":...,"modifiedAt":...}
Let's delete the second blog with the ID,
0
. Since our service method doesn't return anything, you won't see any response.$ curl --request DELETE 'localhost:8080/blogs/0'
Let's retrieve the blog post list to see if the post has been deleted successfully.
$ curl --request GET 'localhost:8080/blogs'
The response contains only one blog post. We can tell that the blog post with the ID
0
has been removed.{"id":1,"title":"Second post for testing","content":"Test deleting a post.","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 for a DELETE operation and used Armeria's annotations; @Delete
, @Param
, @ExceptionHandler
and @Blocking
.
We've come to the end of this tutorial. Next, try adding more service methods to the tutorial or have a go at developing a service of your own.