Francesco
Francesco

Reputation: 1802

REST add subresources

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

Answers (1)

Paul Samsotha
Paul Samsotha

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

Related Questions