2014-04-22 14 views
16

Sto cercando di creare un bind personalizzato http per il mio servizio restful. Si prega di vedere l'esempio qui sotto.jersey 2: Come creare un binding param personalizzato HTTP

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

} 

Si può vedere che c'è un'annotazione UserAuthHeaderParam nella firma della funzione. Quello che voglio fare è avere un bind personalizzato http diverso da quello standard javax.ws.rs. * Param.

devo cercare di attuare org.glassfish.hk2.api.InjectionResolver che fondamentalmente estrarre il valore da intestazione http:

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

    return "Hello World"; 
} 
... 

} 

Quando chiamo il servizio riposante, il server ottiene sotto eccezioni. Indica che il framework non riesce a risolvere il parametro nella firma della funzione:

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 

Please help. Qualsiasi consiglio è apprezzato. Faccio molte ricerche su google ma non riesco a farlo funzionare. Jersey 2.*. How to replace InjectableProvider and AbstractHttpContextInjectable of Jersey 1.* potrebbe essere la domanda simile.

- AGGIORNAMENTI: Io uso AbstractBinder di legare il mio risolutore di 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"); 

} 

} 

Grazie!

+0

Dov'è il @Service? –

+0

@MingtaoZhang I registro un AbstractBinder. modifica la mia domanda per aggiungere questi dettagli – yzandrew

+0

quale versione di jersey jar stai usando? lo stai eseguendo su qualsiasi server di applicazioni o grizzly? –

risposta

13

Se tutto ciò che si desidera è passare il valore direttamente dall'intestazione al metodo, non è necessario creare annotazioni personalizzate.Diciamo che hai un colpo di testa Authorization, allora si può facilmente accedere dichiarando il tuo metodo come questo:

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

potete provarlo chiamando curl, per esempio

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

Dato che la risposta alla tua domanda, come creare l'associazione personalizzata è la seguente.

In primo luogo si deve dichiarare l'annotazione in questo modo:

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

Avere l'annotazione dichiarato è necessario definire come sarà risolto. Dichiarare il provider Valore di fabbrica (questo è dove avrete accesso ai parametri di intestazione - vedi il mio commento):

@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"; 
      } 
     }; 
    } 
} 

Ora dichiara un risolutore di iniezione

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

e un raccoglitore per la configurazione

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

ora l'ultima cosa, nel vostro ResourceConfig aggiungere register(new HeaderParamResolverBinder()), come questo

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

Dato che, si dovrebbe essere ora in grado di utilizzare il valore come si voleva:

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

Spero che questo aiuta.

+0

Funziona quando si mischiano con altre annotazioni esistenti, per esempio @PathParam nello stesso metodo? –

+0

@DhanaKrishnasamy beh non puoi avere annotazioni sullo stesso argomento del metodo ovviamente, ma puoi avere un metodo come '@Path (" {id} ") public String getResult (@UserAuthHeaderParam String utente, @PathParam (" id ") ID intero) ', se è quello che stai chiedendo? – lpiepiora

+0

@lpiepiora Grazie per la soluzione! Quello di cui ho realmente bisogno è legare tutte le intestazioni HTTP in un oggetto. Quindi devo creare un binding personalizzato. E mi dispiace per la risposta tardiva.Stavo lavorando su diversi progetti quindi non ho avuto la possibilità di provare la soluzione. Risponderò una volta che lo verificherò. Dovrebbe essere presto la prossima settimana! Grazie ancora! – yzandrew

2

Non so come risolvere l'eccezione. Tuttavia, posso proporti un modo diverso di fare la stessa cosa. Spero possa essere d'aiuto.

Ho affrontato esattamente lo stesso problema: ho bisogno di parametri aggiuntivi nell'intestazione http (btw, anche relativa all'autenticazione). Inoltre, ho bisogno di inviarli in ogni chiamata, dal momento che voglio fare un'implementazione di "tipico" riposo, senza mantenere una sessione.

Sto usando Jersey 2.7 - ma direi che dovrebbe funzionare in 2.0. Ho seguito la loro documentazione

E 'abbastanza chiaro lì, ma comunque copio-incolla la mia implementazione qui sotto. Funziona bene. È vero che esistono altri modi per proteggere un servizio di assistenza, ad esempio: http://www.objecthunter.net/tinybo/blog/articles/89

Ma dipendono dall'implementazione del server delle applicazioni e dal database che si utilizza. Il filtro, a mio parere, è più flessibile e più facile da implementare.

Il copia-incolla: ho definito un filtro per l'autenticazione, che si applica a ogni chiamata e viene eseguito prima del servizio (grazie a @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() { 
       //... 
      } 
     }); 

    } 

} 

È necessario includere questa classe filtro nell'implementazione di ResourceConfig,

public class MyResourceConfig extends ResourceConfig { 

    public MyResourceConfig() { 

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

    } 

} 

Speranza che aiuta!

+0

Grazie per la soluzione! Devo attenermi al binding personalizzato. Si prega di vedere i miei commenti a lpiepiora per la ragione di dettaglio. Tuttavia, la soluzione potrebbe aiutare altre persone con problemi simili. – yzandrew

1

Se è necessario recuperare tutta l'associazione di intestazioni HTTP in un oggetto, una soluzione potrebbe essere utilizzare l'annotazione @Context per ottenere javax.ws.rs.core.HttpHeaders; che contiene l'elenco di tutte le intestazioni delle richieste.

@POST 
@Path("/user/{userId}/orders") 
@Produces(MediaType.APPLICATION_JSON) 
@Consumes(MediaType.APPLICATION_JSON) 
public MyResult foo(@PathParam("userId") String someString, @Context HttpHeaders headers){ 
// You can list all available HTTP request headers via following code : 
    for(String header : headers.getRequestHeaders().keySet()){ 
    System.out.println(header); 
    } 
} 
+0

Grazie per la risposta. Penso che l'associazione personalizzata sia un modo migliore per me. Posso associare le intestazioni http/parametri query/percorso in un oggetto protobuff, che viene quindi utilizzato come parte dell'oggetto modello nella mia logica aziendale. – yzandrew

0

qui è la mia attuale implementatipn di classe UserAuthHeaderParamValueFactoryProvider

import javax.inject.Inject; 
import javax.inject.Singleton; 

import org.glassfish.hk2.api.Factory; 
import org.glassfish.hk2.api.ServiceLocator; 
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; 
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider; 
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; 

    import org.glassfish.jersey.server.model.Parameter; 

    @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 AbstractContainerRequestValueFactory<String>() { 
       @Override 
       public String provide() { 
        //you can use get any header value. 
        return getContainerRequest().getHeaderString("Authorization"); 
       } 

      }; 
     } 
Problemi correlati