Reputation: 1802
studying REST I stumbled into sub-resources and I did some experiments, but no success. Here is the code, I'm using RESTeasy 3.0.9.
@Path(value="/user")
public interface UsersHttp {
@GET
@Produces(MediaType.APPLICATION_JSON)
public User[] getUsers();
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public User getUser(@PathParam("id") Long id);
...
}
this interface is implemented by this session-bean:
@Stateless
public class UsersHttpImpl implements UsersHttp {
@Inject
private UserPersistence persist;
@Inject
private MediaUtils mediaUtils;
@Override
public User[] getUsers() {
return persist.getUsers(null);
}
@Override
public User getUser(Long id) {
return persist.getUser(id);
}
...
}
This is a snippet of the User
class:
@SuppressWarnings("serial")
@Entity
@Table(name = "usr_user")
@XmlRootElement
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "usr_id")
private Long id;
@Column(name = "usr_name", nullable = false)
private String name;
...
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Path("/name")
public String getName() {
return name;
}
...
}
I can successfully get a User
by MyApplication/user/1
, but when I do GET
with MyApplication/user/1/name
I get a javax.ws.rs.NotFoundException
, I was expecting Alice
instead. What did I miss?
Upvotes: 0
Views: 1990
Reputation: 209122
You don't have anything that maps to the name
part in the request URI. @Path
doesn't work like that (putting it in the model class).
If you want to filter just the name, I would suggest using query params or matrix params (see more here). Based on the params provided you can create a partial representation. A good article can be read here. A simple example might be something like
// ...users/1234?filter=name,age
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public UserDTO getUser(@PathParam("id") Long id,
@QueryParam("filter") String filter) {
User user = service.findUserById(id);
String[] include = filter.split(",");
UserDTO dto = new UserDTO();
for (String s: include) {
// set dto fields based on the filter
// example in psuedo code
if (s equals name)
dto.setName(user.getName());
}
return dto;
}
Depending on the provider for marshalling, you may need to configure some settings to ignore null fields.
That said, this may not even be your main issue/concern. If you are just trying to get a grasp on sub-resources, you are a bit off. In the context of JAX-RS, this is not a way to obtain "parts of a resource".
Basically, a sub-resource/sub-resource method is any method annotated with @Path
. The class is root resource and any method not annotated with @Path
is a action that can be performed on the root resource. Any method annotated with @Path
is a sub-resource of the root resource. In your case user
(which I would make users
) is a "collection resource" and using a "@Path("{id}")` on the method, you are saying you want the sub-resource, which is a single user.
Then there is the concept of sub-resource locators, which works a little differently. Here we are saying that another resource class can be located via the root-resource class something like
@Path("/users")
public class UserResource {
@GET
@Path("{id}/friends")
public FriendsResource getFriendsResource(@PathParam("id") lond id) {
User user = service.getUserById(id);
return new FriendsResource(user);
}
}
public class FriendsResource {
User user;
public FriendResource(User user) {
this.user = user;
}
@GET
public List<Friend> getAllFriends() {
return user.getFriends();
}
}
The above would cause the URI ...users/1234/friends
to go to the FriendsResource
class, and invoke getAllFriends()
.
That's pretty much the gist of sub-resources in the context of JAX-RS.
Upvotes: 1