1

我的API使用Jersey 2,現在我想支持國際化。我知道我的客戶應該指定Accept-Language參數,但我想了解如何正確處理它。僅支持有限數量的語言

我們假設我的API只能處理FRENCHENGLISH語言。我知道我可以用下面的代碼檢索首選區域:

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

的問題是當首選區域不被我的API支持。假設我的客戶發送給我Accept-Language: da, en-gb;q=0.8, en;q=0.7,根據w3c,它基本上是指:"I prefer Danish, but will accept British English and other types of English."。由於首選語言環境只返回最期望的語言環境,有沒有辦法通過我的API選擇第一種支持的語言?我想在一個地方處理它(即在Filters),而不是在每個資源中。

回答

2

獲取語言環境的一種方法是使用HttpHeaders#getAcceptableLanguages()

獲取響應可接受的語言列表。

如果未指定可接受的語言,則返回包含單個通配符Locale實例(語言字段設置爲「*」)的只讀列表。

返回: 一個根據它們的q值排序的可接受語言的只讀列表,首先是最高優先級。

你可以注入HttpHeaders幾乎任何地方,使用@Context

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

如果你想獲得一個filter列表中,您還可以從ContainerRequestContext

@Override 
public void filter(ContainerRequestContext requestContext) throw .. { 
    List<Locales> langs = requestContext.getAcceptableLanguages(); 
} 
得到的語言環境列表列表

如果您想在資源方法中使用Locale,但不想在該方法中執行所有區域「解析」,則y您可以同時使用一些依賴注入,並創建一個Factory,在那裏你可以注入他HttpHeaders和解決現場存在

另請參見:Dependency injection with Jersey 2.0

下面是一個使用最後兩個組合的完整的測試案例我提到的有關使用沿着Factory的過濾器和依賴注入的觀點,以便您可以將解析後的Locale注入資源方法。該示例使用僅允許英語的虛擬語言環境解析器。我們解決了現場後,我們將它設置成一個請求上下文屬性,並從Factory使內檢索,我們可以把它注射到資源的方法

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

另請參見:How to inject an object into jersey request context?

讓我知道是否有什麼其他的你想我解釋一下這個例子

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