1

Meine API verwendet Jersey 2, und jetzt möchte ich die Internationalisierung unterstützen. Ich verstehe, dass mein Klient den Accept-Language Parameter spezifizieren sollte, aber ich möchte verstehen, wie man es richtig behandelt.Unterstützung nur eine begrenzte Anzahl von Sprachen

Angenommen, meine API sollte nur die Sprachen FRENCH und ENGLISH verarbeiten. Ich weiß, dass ich den bevorzugten locale mit dem folgenden Code abrufen kann:

@GET 
@Path("a-path") 
public Response doSomething(@Context HttpServletRequest request) { 
    Locale locale = request.getLocale(); 
    // ... 
} 

Das Problem ist, wenn die bevorzugtes Gebietsschema nicht durch meine API unterstützt wird. Lassen Sie uns sagen, dass mein Klient mich Accept-Language: da, en-gb;q=0.8, en;q=0.7, entsprechend w3c sendet, bedeutet es im Wesentlichen: "I prefer Danish, but will accept British English and other types of English.". Da die bevorzugte Gebietsschema nur die meisten erwarteten Gebietsschema zurückgeben, gibt es eine Möglichkeit, die erste unterstützte Sprache von meiner API auszuwählen? Ich möchte es an einem Ort (d. H. In Filters) und nicht in allen Ressourcen behandeln.

Antwort

2

Um die Gebietsschemas zu erhalten, verwenden Sie HttpHeaders#getAcceptableLanguages().

Hier erhalten Sie eine Liste der Sprachen, die für die Antwort akzeptabel sind.

Wenn keine zulässigen Sprachen angegeben sind, wird eine schreibgeschützte Liste mit einer einzelnen Platzhalterinlands-Instanz (mit einem auf "*" gesetzten Sprachfeld) zurückgegeben.

Rückgabe: eine Liste von nur zulässigen Sprachen sortiert nach ihrem q-Wert, mit höchster Präferenz zuerst.

Sie HttpHeaders ziemlich überall injizieren kann, mit @Context

public Response doSomething(@Context HttpHeaders headers) { 
    List<Locale> langs = headers.getAcceptableLanguages(); 

Wenn Sie von der ContainerRequestContext

@Override 
public void filter(ContainerRequestContext requestContext) throw .. { 
    List<Locales> langs = requestContext.getAcceptableLanguages(); 
} 
erhalten die Liste in einer filter, Sie können auch die Liste Liste der Schauplätze bekommen wollte

Wenn Sie die Locale in der Ressourcenmethode verwenden wollten, aber nicht das gesamte Gebietsschema "auflösen" in der Methode, y ou kann einige Dependency Injection, verwendet und ein Factory schaffen, wo man ihm HttpHeaders und lösen Sie das Gebietsschema gibt

Siehe auch injizieren:Dependency injection with Jersey 2.0

Nachfolgend finden Sie ein komplettes Testfallbeispiel eine Kombination aus beide letzten Verwendung Punkte, die ich über die Verwendung eines Filters und Abhängigkeitsinjektion entlang einer Factory erwähnt, so dass Sie einfach die aufgelöste Locale in die Ressource-Methode injizieren können. Das Beispiel verwendet einen Dummy-Locale-Resolver, der nur Englisch erlaubt.Nachdem wir das Gebietsschema beheben, legen wir es in einen Anforderungskontext-Eigenschaft und Abrufen aus dem Inneren des Factory so, dass wir es in die Ressource-Methode

@GET 
public String get(@Context Locale locale) { 
    return locale.toString(); 
} 

Siehe auch injizieren: mich How to inject an object into jersey request context?

Lassen wissen, ob es noch etwas gibt, was ich über das Beispiel erklären möchte

import java.io.IOException; 
import java.util.List; 
import java.util.Locale; 
import java.util.logging.Logger; 
import javax.inject.Inject; 
import javax.inject.Singleton; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerRequestFilter; 
import javax.ws.rs.container.PreMatching; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.Response; 
import javax.ws.rs.ext.Provider; 

import org.glassfish.hk2.api.Factory; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.filter.LoggingFilter; 
import org.glassfish.jersey.process.internal.RequestScoped; 
import org.glassfish.jersey.server.ResourceConfig; 
import org.glassfish.jersey.test.JerseyTest; 
import org.junit.Test; 

import static org.hamcrest.CoreMatchers.is; 
import static org.junit.Assert.assertThat; 

/** 
* Stack Overflow question https://stackoverflow.com/q/36871274/2587435 
* 
* Run this like any other JUnit test. Only one required test dependency: 
* 
* <dependency> 
*  <groupId>org.glassfish.jersey.test-framework.providers</groupId> 
*  <artifactId>jersey-test-framework-provider-inmemory</artifactId> 
*  <version>${jersey2.version}</version> 
* </dependency> 
* 
* @author Paul Samsotha 
*/ 
public class AcceptLanguageTest extends JerseyTest { 

    @Path("language") 
    public static class TestResource { 

     @GET 
     public String get(@Context Locale locale) { 
      return locale.toString(); 
     } 
    } 

    public static interface LocaleResolver { 
     Locale resolveLocale(List<Locale> locales); 
    } 

    // Note: if you look in the javadoc for getAcceptableLanguages() 
    // you will notice that it says if there is not acceptable language 
    // specified, that there is a default single wildcard (*) locale. 
    // So this implementation sucks, as it doesn't check for that. 
    // You will want to make sure to do so! 
    public static class DefaultLocaleResolver implements LocaleResolver { 

     @Override 
     public Locale resolveLocale(List<Locale> locales) { 
      if (locales.contains(Locale.ENGLISH)) { 
       return Locale.ENGLISH; 
      } 
      return null; 
     } 
    } 

    @Provider 
    @PreMatching 
    public static class LocaleResolverFilter implements ContainerRequestFilter { 

     static final String LOCALE_PROPERTY = "LocaleResolverFilter.localProperty"; 

     @Inject 
     private LocaleResolver localeResolver; 

     @Override 
     public void filter(ContainerRequestContext context) throws IOException { 
      List<Locale> locales = context.getAcceptableLanguages(); 
      Locale locale = localeResolver.resolveLocale(locales); 
      if (locale == null) { 
       context.abortWith(Response.status(Response.Status.NOT_ACCEPTABLE).build()); 
       return; 
      } 
      context.setProperty(LOCALE_PROPERTY, locale); 
     } 
    } 

    public static class LocaleFactory implements Factory<Locale> { 

     @Context 
     private ContainerRequestContext context; 

     @Override 
     public Locale provide() { 
      return (Locale) context.getProperty(LocaleResolverFilter.LOCALE_PROPERTY); 
     } 

     @Override 
     public void dispose(Locale l) {} 
    } 

    @Override 
    public ResourceConfig configure() { 
     return new ResourceConfig(TestResource.class) 
       .register(LocaleResolverFilter.class) 
       .register(new AbstractBinder() { 
        @Override 
        protected void configure() { 
         bindFactory(LocaleFactory.class) 
           .to(Locale.class).in(RequestScoped.class); 
         bind(DefaultLocaleResolver.class) 
           .to(LocaleResolver.class).in(Singleton.class); 
        } 
       }) 
       .register(new LoggingFilter(Logger.getAnonymousLogger(), true)); 
    } 

    @Test 
    public void shouldReturnEnglish() { 
     final String accept = "da, en-gb;q=0.8, en;q=0.7"; 
     final Response response = target("language").request() 
       .acceptLanguage(accept) 
       .get(); 
     assertThat(response.readEntity(String.class), is("en")); 
    } 

    @Test 
    public void shouldReturnNotAcceptable() { 
     final String accept = "da"; 
     final Response response = target("language").request() 
       .acceptLanguage(accept) 
       .get(); 
     assertThat(response.getStatus(), is(Response.Status.NOT_ACCEPTABLE.getStatusCode())); 
    } 
}