【问题标题】:How to parse request parameter (query and path param) and request body in same POJO in REST API如何在 REST API 的同一个 POJO 中解析请求参数(查询和路径参数)和请求正文
【发布时间】:2018-09-18 20:39:06
【问题描述】:

我有一个接受请求正文和路径参数的休息 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。 如何在同一个类中同时解析请求体和参数?

【问题讨论】:

    标签: java rest jersey put path-parameter


    【解决方案1】:

    您可以使用自定义注释和InjectionResolver 来做到这一点。 InjectionResolver 所做的是允许您使用自己的注释创建自定义注入点。所以你可以做类似的事情

    public class Users {
    
        @PathParam(username)
        private String userName;
    
        @Body
        private String a;
    }
    

    在实现InjectionResolver 时,您将使用ContainerRequest#readEntity(Class) 方法从ContainerRequest 中获取实际主体。您可以通过对可以在InjectionResolver 中获得的Field 进行一些反思来确定Class 是否通过。下面是使用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;
            }
        }
    }
    

    【讨论】:

    • 这个解决方案看起来很有趣。 @Paul Samsotha:我的问题是 swagger 没有在其 json 生成的文件中显示正文(或者在 swagger ui 的末尾)你有同样的问题吗?
    • @JulienO 是的,这不适用于 Swagger。 Swagger 将无法识别自定义注释。
    • 好的,谢谢...希望此功能将很快被添加到开箱即用的状态并被 swagger 使用...
    猜你喜欢
    • 1970-01-01
    • 2021-08-22
    • 2012-04-11
    • 1970-01-01
    • 2022-08-20
    • 2013-12-17
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多