Setting a Jersey response status code from interface without returning Response

孤人 提交于 2019-12-11 10:08:54

问题


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).


回答1:


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.




回答2:


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);


来源:https://stackoverflow.com/questions/49446798/setting-a-jersey-response-status-code-from-interface-without-returning-response

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!