如何在REST API的同一POJO中解析请求参数(查询和路径参数)和请求正文

时间:2018-09-18 20:39:07

标签: java rest jersey put path-parameter

我有一个rest API(PUT动词),它可以接受请求正文和路径参数:

例如:

curl --data {a:1,b:2} -X PUT“ https://example.com/users/ {username} / address / {addressname}”

我正在尝试在一个POJO中同时获取请求正文和路径参数

Response myAPI(@BeanParam Users user){
   system.out.println(user.username);
   system.out.println(user.a);

用户分类

public class Users{

    @PathParam(username)
    private String userName;
    ......

    private String a;
  ......
}

但是我得到user.a的值为null。 如何在同一个类中解析请求正文和参数?

1 个答案:

答案 0 :(得分:0)

您可以使用自定义注释和InjectionResolver进行此操作。 InjectionResolver的作用是允许您使用自己的注释创建自定义注入点。所以你可以做类似的事情

public class Users {

    @PathParam(username)
    private String userName;

    @Body
    private String a;
}

在实现InjectionResolver时,您将使用ContainerRequest方法从ContainerRequest#readEntity(Class)获取实际主体。您可以通过对Class内获得的Field进行一些思考来确定要通过的InjectionResolver。以下是使用Jersey Test Framework的完整示例。像其他任何JUnit测试一样运行它。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;

import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

/**
 * Only one required dependency to run this test. Note this is using 2.25.1.
 * If you are using 2.26 or later, the implementation will be different,
 * and this will not work. You need to use the Jersey packaged `InjectionResolver`,
 * not the HK2 one. And you will also need to bind that `InjectionResolver`
 * to `GenericType` instead of `TypeLiteral` in the `AbstractBinder#configure()`.
 *
 * <dependency>
 *   <groupId>org.glassfish.jersey.test-framework.providers</groupId>
 *   <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
 *   <version>2.25.1</version>
 *   <scope>test</scope>
 * </dependency>
 */
public class BeanParamTest extends JerseyTest {

    @Path("test")
    @Consumes("application/json")
    @Produces("application/json")
    public static class TestResource {

        @POST
        @Path("{username}")
        public String post(@BeanParam ModelBean bean) {
            return bean.toString();
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(TestResource.class)
                .register(new AbstractBinder() {
                    @Override
                    protected void configure() {
                        bind(BodyInjectionResolver.class)
                                .to(new TypeLiteral<InjectionResolver<Body>>() {})
                                .in(Singleton.class);
                    }
                });
    }

    @Test
    public void testIt() {
        final Response res = target("test/peeskillet")
                .request()
                .post(Entity.json("{\"foo\":\"bar\"}"));
        System.out.println(res.readEntity(String.class));
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface Body {}

    public static class ModelBean {

        @PathParam("username")
        private String username;

        @Body
        private String body;

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getBody() {
            return body;
        }

        public void setBody(String body) {
            this.body = body;
        }

        @Override
        public String toString() {
            return "ModelBean{" +
                    "username='" + username + '\'' +
                    ", body='" + body + '\'' +
                    '}';
        }
    }

    public static class BodyInjectionResolver implements InjectionResolver<Body> {

        @Inject
        private javax.inject.Provider<ContainerRequest> requestProvider;

        @Override
        public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) {
            if (injectee.getParent().isAnnotationPresent(Body.class)) {
                AnnotatedElement parent = injectee.getParent();
                if (parent instanceof Field) {
                    Class<?> entityType = ((Field) parent).getType();
                    return requestProvider.get().readEntity(entityType);
                }

            }
            return null;
        }

        @Override
        public boolean isConstructorParameterIndicator() {
            return false;
        }

        @Override
        public boolean isMethodParameterIndicator() {
            return false;
        }
    }
}