1

As an illustration, let's suppose I'm creating an HTTP REST API that behaves as follows:

Return a collection of all employees:

GET /employees/

[
    {
        "id": 1,
        "name": "John",
        "roles": [
            {
                "id": 20,
                "responsibility": 1000
            },
            ...
        ]
    },
    ...
]

Return a representation of employee with id 1:

GET /employees/1/

{
    "id": 1,
    "Name": "John",
    "roles": [
        {
            "id": 20,
            "responsibility": 1000
        },
        ...
    ]
}

Return a collection of the employee's roles:

GET /employees/1/roles

[
    {
        "id": 20,
        "responsibility": 1000
    },
    ...
]

Return a representation of role with id 20:

GET /employees/1/roles/20

{
    "id": 20,
    "responsibility": 1000
}

In this way we can traverse the tree of the employee representation. There are likely many other employee endpoints besides /roles.

Using Jersey I split this up into a root resource with several subresources, for example:

@Path("/employees")
public class EmployeesResource() {
    ...
    @GET
    @Path("/{employeeId}/roles")
    public RolesResource getRoles(@PathParam("employeeId") long employeeId) {
        return new RolesResource(employeeId);
    }
}

This produces the desired JSON result when I GET /employees and /employees/1 but when I GET /employees/1/roles I get the response body:

{
    "roles": [
        {
            "id": 20,
            "responsibility": 1000
        },
        ...
    ]
}

Jersey has wrapped my collection of Role representations in some object. Note: The object seems to be connected to the name of the subresource resource method within RolesResource (for example, if the method is named getRoles() I get {"roles": [...]}, if the method is named getFoo() I get {"foo": [...]}).

My Question: There must be a reason Jersey does this. Why would I want my representation to be wrapped like this? And if there isn't a good reason, how can I get rid of this?

Edit: I'm using Dropwizard version 0.8.2, which it looks like from maven is pulling in Jersey 2.19. It uses Jackson as the JSON provider - again, from maven it looks like the version is 2.5.1. No web.xml as this is a dropwizard application.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
tytk
  • 2,082
  • 3
  • 27
  • 39
  • Returning the top-level value as an array is a security vulnerability. That's why Jersey won't do it. See http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk – Ian McLaird Oct 04 '15 at 14:20
  • That's interesting, I'd never heard about that vulnerability before. Isn't it pretty common for REST endpoints to serve top-level arrays? For example, https://www.reddit.com/r/harrypotter/comments/3fecge/.json. Also, Jersey does serve a top-level array from the EmployeesResource at `/employees/`, it just wraps any subresources. That's why I'm confused. – tytk Oct 04 '15 at 17:13

2 Answers2

0

I don't know dropwizard but I do know jackson and I'm currently using jersey 2.21, so I hope my setup is closed enough to yours.

I guess you have constructed your RolesResource class somewhat similar to this (if it is not the case, please post it!)

public class RolesResource {
    private Role[] roles;

    //constructor, getters, setters...
}

where Role would be something like,

public class Role {
    private Integer id;
    private Integer responsibility;

    //constructor, getters, setters...
}

If this is the case, if you return RolesResource as an output of your service, you will just get the json you describe,

{
    "roles": [
        {
           ...
        },
        ...
    ]
}

since the object has actually an array Role[] with name roles.

However, you could also return Role[] as an output of your service,

@Path("/employees")
public class EmployeesResource() {
    ...
    @GET
    @Path("/{employeeId}/roles")
    public Role[] getRoles(@PathParam("employeeId") long employeeId) {
        return (new RolesResource(employeeId)).getRoles();
    }
}

And then the output will be just array of objects of type Role.

[
    {
        "id": ...,
        "responsibility": ...
    },
    ...
]

As for your question, does either make more sense than the other? I guess it is a matter of choice; I usually prefer to encapsulate so that it is easier to check on the client side (thus I go for the first solution), but I don't think it makes a big difference.

Hope it helps.

lrnzcig
  • 3,868
  • 4
  • 36
  • 50
0

I figured it out. I had annotated the subresource locator (EmployeesResource.getRoles() in this example) with @GET. Removing that annotation produced the desired result. The methods within RolesResource are annotated with HTTP method names.

tytk
  • 2,082
  • 3
  • 27
  • 39