finrod
finrod

Reputation: 82

Setting a Jersey response status code from interface without returning Response

I am trying to set a response status of the following Jersey REST endpoint

@Path("/roles")
public interface IRoleService {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    Role create(Role role);

}

Since it creates a new resource it would be appropriate if it returned Status code 201 but currently it returns 200.

The only way I found how to set the status code is to have the method return a javax.ws.rs.core.Response and set it there, but I really do not want all of my interfaces to return a generic Response instead of the actual response object (in this case Role).

Upvotes: 2

Views: 3209

Answers (2)

riteshmaurya
riteshmaurya

Reputation: 373

Removed ElementType.Type.

    @NameBinding
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Status {
        int DEFAULT_CODE = 0;
        int code() default DEFAULT_CODE;
    }

statusFilter class:

    @Provider
    public class StatusFilter implements ContainerResponseFilter {

        private static Logger logger = LoggerFactory.getLogger(StatusFilter.class);

        @Context
        private ResourceInfo resourceInfo;

        @Override
        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
            Status status = resourceInfo.getResourceMethod().getAnnotation(Status.class);
            if(status!=null){
                int code = status.code();
                if(code != Status.DEFAULT_CODE && responseContext.getStatus() == 200) {
                    responseContext.setStatus(code);
                }
            }
        }
    }

Then use it in the resource interface method declaration

@POST
@Status(code = 201)
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
GetUserResponse createUser(UserRequest userRequest);

Upvotes: 0

Paul Samsotha
Paul Samsotha

Reputation: 208994

One way would be to create a custom annotation and using a response filter to set the status. For example

Annotation

@NameBinding
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
    int DEFAULT_CODE = 0;

    int code() default DEFAULT_CODE;
}

ContainerResponseFilter

@Status
@Provider
public class StatusFilter implements ContainerResponseFilter {

    @Context
    private ResourceInfo info;

    @Override
    public void filter(ContainerRequestContext req, ContainerResponseContext res) throws IOException {
        Status status = getInterfaceAnnotation(info.getResourceMethod());
        if (status != null) {
            int code = status.code();
            if (code != Status.DEFAULT_CODE && res.getStatus() == 200) {
                res.setStatus(code);
            }
        }
    }

    private static Status getInterfaceAnnotation(Method resourceMethod) {
        String methodName = resourceMethod.getName();
        Class<?>[] paramTypes = resourceMethod.getParameterTypes();
        Class<?> iface = resourceMethod.getDeclaringClass().getInterfaces()[0];
        Method ifaceMethod;
        try {
            ifaceMethod = iface.getDeclaredMethod(methodName, paramTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
        return ifaceMethod.getAnnotation(Status.class);
    }
}

In the filter, we get the method with ResourceInfo and do some reflection to get the @Status annotation. The from there, can grab the status code and set it on the response.

Since it is a Name Binding filter, it will only be called for methods that are annotated with it. See more here.

Then to use it, just add the annotation to the method.

public interface ITestResource {
    @GET
    @Status(code=201)
    String get();
}

The same could be done for headers if you need to add some custom headers.

Upvotes: 3

Related Questions