球衣 2:如何创建自定义 HTTP 参数绑定

2022-09-03 02:01:45

我正在尝试为我的宁静服务创建自定义 http 参数绑定。请参阅下面的示例。

@POST
@Path("/user/{userId}/orders")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(@PathParam("userId") String someString, @UserAuthHeaderParam String authString){

}

您可以看到函数签名中有一个 UserAuthHeaderParam 注释。我想做的是有一个自定义的http参数绑定,而不是标准的javax.ws.rs.*Param。

我尝试实现org.glassfish.hk2.api.InjectionResolver,它基本上从http header中提取值:

public class ProtoInjectionResolver implements InjectionResolver<UserAuthHeaderParam>{
...
@Override
public Object resolve(Injectee injectee, ServiceHandle< ? > root)
{

    return "Hello World";
}
...

}

当我调用 restful 服务时,服务器会得到以下异常。它指示框架无法解析函数签名中的参数:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyResource,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2136594195), 

java.lang.IllegalArgumentException: While attempting to resolve the dependencies of rs.server.MyResource errors were found

请帮忙。任何建议都将不胜感激。我确实在谷歌上做了很多搜索,但未能使其工作。球衣 2.*.如何替换 InjectableProvider 和 AbstractHttpContextInjectable of Jersey 1.* 可能是类似的问题。

- 更新:我使用AbstractBinder将我的解析器绑定到UserAuthHeaderParam:

public class MyApplication extends ResourceConfig
{

public MyApplication()
{
    register(new AbstractBinder()
    {
        @Override
        protected void configure()
        {
            // bindFactory(UrlStringFactory.class).to(String.class);
            bind(UrlStringInjectResolver.class).to(new TypeLiteral<InjectionResolver<UrlInject>>()
            {
            }).in(Singleton.class);
        }
    });
    packages("rs");

}

}

谢谢!


答案 1

如果您只想将值直接从标头传递到方法,则无需创建自定义批注。假设您有一个标头,然后您可以通过像这样声明您的方法来轻松访问它:Authorization

@GET
public String authFromHeader(@HeaderParam("Authorization") String authorization) {
    return "Header Value: " + authorization + "\n";
}

您可以通过调用 来测试它,例如curl

$ curl --header "Authorization: 1234" http://localhost:8080/rest/resource
Header Value: 1234

鉴于您的问题的答案,如何创建自定义绑定如下所示。

首先,您必须像这样声明您的注释:

@java.lang.annotation.Target(PARAMETER)
@java.lang.annotation.Retention(RUNTIME)
@java.lang.annotation.Documented
public @interface UserAuthHeaderParam {
}

声明注释后,必须定义如何解析它。声明值工厂提供程序(这是您可以访问标头参数的位置 - 请参阅我的评论):

@Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {

    @Inject
    protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
        super(mpep, locator, Parameter.Source.UNKNOWN);
    }

    @Override
    protected Factory<?> createValueFactory(Parameter parameter) {
        Class<?> classType = parameter.getRawType();

        if (classType == null || (!classType.equals(String.class))) {
            return null;
        }

        return new AbstractHttpContextValueFactory<String>() {
            @Override
            protected String get(HttpContext httpContext) {
                // you can get the header value here
                return "testString";
            }
        };
    }
}

现在声明注入旋转变压器

public class UserAuthHeaderParamResolver extends ParamInjectionResolver<UserAuthHeaderParam> {
    public UserAuthHeaderParamResolver() {
        super(UserAuthHeaderParamValueFactoryProvider.class);
    }
}

和用于您的配置的活页夹

public class HeaderParamResolverBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bind(UserAuthHeaderParamValueFactoryProvider.class)
                .to(ValueFactoryProvider.class)
                .in(Singleton.class);

        bind(UserAuthHeaderParamResolver.class)
                .to(new TypeLiteral<InjectionResolver<UserAuthHeaderParam>>() {})
                .in(Singleton.class);
    }
}

现在最后一件事,在你的资源配置中添加,就像这样register(new HeaderParamResolverBinder())

@ApplicationPath("rest")
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new HeaderParamResolverBinder());
        packages("your.packages");
    }
}

鉴于此,您现在应该能够根据需要使用该值:

@GET
public String getResult(@UserAuthHeaderParam String param) {
    return "RESULT: " + param;
}

我希望这有帮助。


答案 2

我不知道如何解决您的异常。但是,我是否可以建议你用另一种方式做同样的事情。我希望它有帮助。

我遇到了完全相同的问题:我需要在http标头中提供额外的参数(顺便说一句,也与身份验证有关)。此外,我需要在每次调用中发送它们,因为我想在不维护会话的情况下执行“典型”的休息实现。

我使用的是泽西岛2.7 - 但我会说它应该在2.0中工作。我已经关注了他们的文档 https://jersey.java.net/documentation/2.0/filters-and-interceptors.html

那里很清楚,但无论如何,我在下面复制粘贴我的实现。它工作正常。诚然,还有其他一些方法来保护休息服务,例如这是一个很好的方法:http://www.objecthunter.net/tinybo/blog/articles/89

但它们取决于应用程序服务器实现和您使用的数据库。在我看来,过滤器更灵活,更容易实现。

复制粘贴:我定义了一个用于身份验证的过滤器,该过滤器适用于每个调用,并在服务之前执行(感谢)。@PreMatching

@PreMatching
public class AuthenticationRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(final ContainerRequestContext requestContext) throws IOException {
        final MultivaluedMap<String, String> headers = requestContext.getHeaders();
        if (headers == null) {
            throw new...
        }

        // here I get parameters from the header, via headers.get("parameter_name")
        // In particular, I get the profile, which I plan to use as a Jersey role
        // then I authenticate
        // finally, I inform the Principal and the role in the SecurityContext object, so that I can use @RolesAllowed later
        requestContext.setSecurityContext(new SecurityContext() {

            @Override
            public boolean isUserInRole(final String arg0) {
                //...
            }

            @Override
            public boolean isSecure() {
                //...
            }

            @Override
            public Principal getUserPrincipal() {
                //...
            }

            @Override
            public String getAuthenticationScheme() {
                //...
            }
        });

    }

}

您必须在 的实现中包含此筛选器类,ResourceConfig

public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {

        // my init
        // my packages
        register(AuthenticationRequestFilter.class); // filtro de autenticación
        // other register

    }

}

希望它有帮助!


推荐