On this page
JAX-RS
Jakarta REST (JAX-RS) is the standard Java API for building RESTful web services. Implementations include Jersey (reference), RESTEasy, and Apache CXF.
Setup (Jersey)
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>3.1.5</version>
</dependency>
Resource Class
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
private final UserService userService;
public UserResource(UserService userService) {
this.userService = userService;
}
@GET
public List<UserDto> getAllUsers(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") int size) {
return userService.findAll(page, size);
}
@GET
@Path("/{id}")
public Response getUser(@PathParam("id") Long id) {
return userService.findById(id)
.map(user -> Response.ok(user).build())
.orElse(Response.status(Response.Status.NOT_FOUND).build());
}
@POST
public Response createUser(@Valid CreateUserRequest request, @Context UriInfo uriInfo) {
UserDto created = userService.create(request);
URI location = uriInfo.getAbsolutePathBuilder().path(created.getId().toString()).build();
return Response.created(location).entity(created).build();
}
@PUT
@Path("/{id}")
public UserDto updateUser(@PathParam("id") Long id, @Valid UpdateUserRequest request) {
return userService.update(id, request);
}
@DELETE
@Path("/{id}")
public Response deleteUser(@PathParam("id") Long id) {
userService.delete(id);
return Response.noContent().build();
}
}
Key Annotations
| Annotation | Purpose |
|---|---|
@Path |
Resource URI path |
@GET/@POST/@PUT/@DELETE |
HTTP method |
@PathParam |
URI template parameter |
@QueryParam |
Query string parameter |
@HeaderParam |
HTTP header value |
@CookieParam |
Cookie value |
@Produces |
Response content type |
@Consumes |
Request content type |
@Context |
Inject runtime context (UriInfo, HttpHeaders) |
Exception Handling
@Provider
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
@Override
public Response toResponse(NotFoundException ex) {
return Response.status(Response.Status.NOT_FOUND)
.entity(Map.of("error", ex.getMessage()))
.type(MediaType.APPLICATION_JSON)
.build();
}
}
Sub-Resources
@Path("/users/{userId}/orders")
public class UserOrderResource {
@GET
public List<OrderDto> getOrders(@PathParam("userId") Long userId) {
return orderService.findByUserId(userId);
}
@POST
public Response createOrder(@PathParam("userId") Long userId, CreateOrderRequest request) {
OrderDto order = orderService.create(userId, request);
return Response.status(Response.Status.CREATED).entity(order).build();
}
}
Filters
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext ctx) throws IOException {
String auth = ctx.getHeaderString("Authorization");
if (auth == null || !auth.startsWith("Bearer ")) {
ctx.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("{\"error\": \"Missing token\"}").build());
}
}
}
JAX-RS vs Spring MVC
| Feature | JAX-RS | Spring MVC |
|---|---|---|
| Standard | Jakarta EE standard | Spring framework |
| Annotations | @Path, @GET |
@RequestMapping, @GetMapping |
| DI | CDI / HK2 | Spring DI |
| Ecosystem | Java EE/Jakarta EE | Spring Boot |
| Best for | Jakarta EE apps | Spring Boot apps |
Best Practices
- Use
@Validwith Bean Validation for input validation - Return
Responseobjects for full control over status and headers - Register exception mappers for consistent error responses
- Use sub-resource locators for nested resources
- For Spring Boot projects, prefer Spring MVC/WebFlux over JAX-RS