Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
作为SpringMVC中URL一部分的区域设置_Url_Spring Mvc_Locale - Fatal编程技术网

作为SpringMVC中URL一部分的区域设置

作为SpringMVC中URL一部分的区域设置,url,spring-mvc,locale,Url,Spring Mvc,Locale,我现在正在寻找一个多语言web应用程序的框架。目前,在我看来,最好的选择是SpringMVC。但我面对的事实是,所有针对开发人员的指导原则都建议以这种方式使用LocaleChangeInterceptor切换语言: 不幸的是,我想避免这样做的原因有很多。我怎样才能使语言代码成为URL的重要组成部分?例如: 谢谢 UPD:我找到了以下解决方案。它还没有完成,但很有效。解决方案由两部分组成——servlet过滤器和区域设置解析器bean。这看起来有点不妥,但我看不到其他方法来解决这个问题 pub

我现在正在寻找一个多语言web应用程序的框架。目前,在我看来,最好的选择是SpringMVC。但我面对的事实是,所有针对开发人员的指导原则都建议以这种方式使用LocaleChangeInterceptor切换语言:

不幸的是,我想避免这样做的原因有很多。我怎样才能使语言代码成为URL的重要组成部分?例如:

谢谢

UPD:我找到了以下解决方案。它还没有完成,但很有效。解决方案由两部分组成——servlet过滤器和区域设置解析器bean。这看起来有点不妥,但我看不到其他方法来解决这个问题

public class LocaleFilter implements Filter
{

    ...

    private static final String DEFAULT_LOCALE = "en";
    private static final String[] AVAILABLE_LOCALES = new String[] {"en", "ru"};

    public LocaleFilter() {} 

    private List<String> getSevletRequestParts(ServletRequest request)
    {
        String[] splitedParts = ((HttpServletRequest) request).getServletPath().split("/");
        List<String> result = new ArrayList<String>();

        for (String sp : splitedParts)
        {
            if (sp.trim().length() > 0)
                result.add(sp);
        }

        return result;
    }

    private Locale getLocaleFromRequestParts(List<String> parts)
    {
        if (parts.size() > 0)
        {
            for (String lang : AVAILABLE_LOCALES)
            {
                if (lang.equals(parts.get(0)))
                {
                    return new Locale(lang);
                }
            }
        }

        return null;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException
    {
        List<String> requestParts = this.getSevletRequestParts(request);
        Locale locale = this.getLocaleFromRequestParts(requestParts);

        if (locale != null)
        {
            request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", locale);

            StringBuilder sb = new StringBuilder();
            for (int i = 1; i < requestParts.size(); i++)
            {
                sb.append('/');
                sb.append((String) requestParts.get(i));
            }

            RequestDispatcher dispatcher = request.getRequestDispatcher(sb.toString());
            dispatcher.forward(request, response);
        }
        else
        {
            request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", new Locale(DEFAULT_LOCALE));
            chain.doFilter(request, response);
        }
    }

    ...
}

public class FilterLocaleResolver implements LocaleResolver
{

    private Locale DEFAULT_LOCALE = new Locale("en");

    @Override
    public Locale resolveLocale(HttpServletRequest request)
    {
        Locale locale = (Locale) request.getAttribute(LocaleFilter.class.getName() + ".LOCALE");
        return (locale != null ? locale : DEFAULT_LOCALE);
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale)
    {
        request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", locale);
    }

}

在Spring3.0中,您可以告诉控制器查找。e、 g


我使用过滤器和拦截器的组合实现了一些非常类似的东西

过滤器提取第一个path变量,如果它是有效的语言环境,则将其设置为请求属性,将其从请求的URI的开头剥离,并将请求转发到新的URI

public class PathVariableLocaleFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(PathVariableLocaleFilter.class);

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    String url = defaultString(request.getRequestURI().substring(request.getContextPath().length()));
    String[] variables = url.split("/");

    if (variables.length > 1 && isLocale(variables[1])) {
        LOG.debug("Found locale {}", variables[1]);
        request.setAttribute(LOCALE_ATTRIBUTE_NAME, variables[1]);
        String newUrl = StringUtils.removeStart(url, '/' + variables[1]);
        LOG.trace("Dispatching to new url \'{}\'", newUrl);
        RequestDispatcher dispatcher = request.getRequestDispatcher(newUrl);
        dispatcher.forward(request, response);
    } else {
        filterChain.doFilter(request, response);
    }
}

private boolean isLocale(String locale) {
    //validate the string here against an accepted list of locales or whatever
    try {
        LocaleUtils.toLocale(locale);
        return true;
    } catch (IllegalArgumentException e) {
        LOG.trace("Variable \'{}\' is not a Locale", locale);
    }
    return false;
}
}
拦截器与
LocaleChangeInterceptor
非常相似,它尝试从请求属性获取区域设置,如果找到区域设置,则将其设置为
LocaleResolver

public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

    Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
    if (newLocale != null) {
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
        if (localeResolver == null) {
            throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
        }
        localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
    }
    // Proceed in any case.
    return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}

@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
    return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
    return new Filter[] { new PathVariableLocaleFilter() };
}
一旦它们就位,您需要将Spring配置为使用拦截器和
LocaleResolver

public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

    Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
    if (newLocale != null) {
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
        if (localeResolver == null) {
            throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
        }
        localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
    }
    // Proceed in any case.
    return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}

@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
    return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
    return new Filter[] { new PathVariableLocaleFilter() };
}
并将过滤器添加到
抽象注释配置Dispatchers ServletInitializer

public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

    Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
    if (newLocale != null) {
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
        if (localeResolver == null) {
            throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
        }
        localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
    }
    // Proceed in any case.
    return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}

@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
    return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
    return new Filter[] { new PathVariableLocaleFilter() };
}

我还没有完全测试过它,但它似乎到目前为止还可以工作,而且您不必触摸控制器就可以接受
{locale}
path变量,它应该可以开箱即用。也许将来我们会有“locale as path variable/subfolder”的Spring automagic解决方案,因为似乎越来越多的网站都在采用它,而且据一些网站介绍。

我发现自己也遇到了同样的问题,在做了大量的研究之后,我最终还使用了一个过滤器和LocaleResolver。步骤指南的步骤:

首先在web.xml中设置过滤器:

<filter>
    <filter-name>LocaleFilter</filter-name>
    <filter-class>yourCompleteRouteToTheFilter.LocaleUrlFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LocaleFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
现在,过滤器向请求注入了两个属性,我们将使用这两个属性来形成区域设置,并从url中剥离语言以正确处理请求。现在我们将定义一个LocaleResolver来更改区域设置。首先,我们修改我们的servlet.xml文件:

<!-- locale Resolver configuration-->
<bean id="localeResolver" class="yourCompleteRouteToTheResolver.CustomLocaleResolver"></bean>

执行此操作时,您无需更改控制器中的任何内容,访问“/en/home”与访问“/home”和使用语言_en.properties文件相同。希望能有所帮助

我最近遇到了同样的问题。所以我希望无状态语言环境不依赖于会话、cookie或其他任何东西,而只是URL

我尝试了前面答案中建议的filter/interceptor/localeResolver解决方案,但这些解决方案不符合我的需要:

  • 静态内容(图像等)
  • 页面部分不依赖于区域设置(管理面板)
  • RestController在同一个应用程序中
  • 多部分文件上传器
出于SEO原因,我还希望避免重复内容(特别是我不希望我的英文内容可以从两个路径访问:/landingPage和/en/landingPage)

最适合我的解决方案是创建LanguageAwareController,然后在我想要支持多个地区的所有控制器中继承它

@Controller
@RequestMapping(path = "/{lang}")
public class LanguageAwareController {
    @Autowired
    LocaleResolver localeResolver;

    @ModelAttribute(name = "locale")
    Locale getLocale(@PathVariable(name = "lang") String lang, HttpServletRequest request,
                 HttpServletResponse response){
        Locale effectiveLocale = Arrays.stream(Locale.getAvailableLocales())
            .filter(locale -> locale.getLanguage().equals(lang))
            .findFirst()
            .orElseGet(Locale::getDefault);
        localeResolver.setLocale(request, response, effectiveLocale);
        return effectiveLocale;
    }
}
在其中一个控制器中使用:

@Controller
public class LandingPageController extends LanguageAwareController{

    private Log log = LogFactory.getLog(LandingPageController.class);

    @GetMapping("/")
    public String welcomePage(Locale locale, @PathVariable(name = "lang") String lang ){
        log.info(lang);
        log.info(locale);
        return "landing";
    }
}

除了提供的答案之外,这里还有一种方法,可以通过实现
ILinkBuilder
,让Thymeleaf在一个又一个上下文路径中自动预先设置语言环境:

@Bean
公共ILinkBuilder路径变量localeLinkBuilder(){
PathVariableLocaleLinkBuilder PathVariableLocaleLinkBuilder=新的PathVariableLocaleLinkBuilder();
pathVariableLocaleLinkBuilder.setOrder(1);
返回pathVariableLocaleLinkBuilder;
}
@豆子
SpringTemplateEngine templateEngine(TeleLabProperties属性、ObjectProvider TemplateResolver、ObjectProvider方言、ObjectProvider链接生成器){
SpringTemplateEngine=新的SpringTemplateEngine();
setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
engine.setRenderHiddenMarkersBefore复选框(properties.isRenderHiddenMarkersBefore复选框());
forEach(引擎::addTemplateResolver);
dialogons.orderedStream().forEach(引擎::adddialogue);
linkBuilders.orderedStream().forEach(引擎::addLinkBuilder);
返回引擎;
}
以下是链接生成器本身:

公共类PathVariableLocaleLinkBuilder扩展了AbstractLinkBuilder{
@自动连线
私有LocaleResolver-LocaleResolver;
@凌驾
公共字符串构建链接(IExpressionContext上下文、字符串基、映射参数){
notNull(上下文,“表达式上下文不能为null”);
if(base==null){
返回null;
}
如果(!IsLink BaseContextRelative(基本)){
返回基地;
}
if(!(IWebContext的上下文实例)){
抛出新的TemplateProcessingException(
链接基\“+base+”\”不能是上下文相关(/…),除非上下文+
用于执行引擎实现“+IWebContext.class.getName()+”接口”);
}
最终HttpServletRequest请求=((IWebContext)上下文).getRequest();
返回“/”+localeResolver.resolveLocale(请求)+base;
}
私有静态布尔值isLinkBaseContextRelative(最终字符序列linkBase){
if(linkBase.length()==0 | | linkBase.charAt(0)!='/')){
返回false;
}
返回linkBase.length()==1 | | linkBase.charAt(1)!='/';
}
}

但在这种情况下,我必须在每个操作中手动切换区域设置。是否可以进行自动切换,例如,使用过滤器?Hi@Gris。。。哈夫