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