Java中的解析接受语言头
请求中的accept language标头通常是一个长而复杂的字符串- 例如Java中的解析接受语言头,java,servlets,http-accept-language,Java,Servlets,Http Accept Language,请求中的accept language标头通常是一个长而复杂的字符串- 例如 有没有一种简单的方法可以用java解析它?或者一个API来帮助我做到这一点?我建议使用来让容器解析Accept语言,而不是自己尝试管理复杂性。这里有一种解析Accept语言头的替代方法,它不需要servlet容器: String header = "en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2"; for (String str : header.split(","))
有没有一种简单的方法可以用java解析它?或者一个API来帮助我做到这一点?我建议使用来让容器解析Accept语言,而不是自己尝试管理复杂性。这里有一种解析Accept语言头的替代方法,它不需要servlet容器:
String header = "en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2";
for (String str : header.split(",")){
String[] arr = str.trim().replace("-", "_").split(";");
//Parse the locale
Locale locale = null;
String[] l = arr[0].split("_");
switch(l.length){
case 2: locale = new Locale(l[0], l[1]); break;
case 3: locale = new Locale(l[0], l[1], l[2]); break;
default: locale = new Locale(l[0]); break;
}
//Parse the q-value
Double q = 1.0D;
for (String s : arr){
s = s.trim();
if (s.startsWith("q=")){
q = Double.parseDouble(s.substring(2).trim());
break;
}
}
//Print the Locale and associated q-value
System.out.println(q + " - " + arr[0] + "\t " + locale.getDisplayLanguage());
}
您可以在此处找到Accept Language标头和相关q值的说明:
非常感谢卡尔·克内赫特尔和迈克·塞缪尔。他们对原始问题的评论为我指明了正确的方向。记录在案,现在有可能:
ServletRequest.getLocale()
如果可用并且不像某些框架那样被覆盖,那么它肯定是最好的选择
对于所有其他情况,Java8都提供了Locale.LanguageRange.parse()
,正如Quiang Li前面提到的那样。但是,这只返回一个语言字符串,而不是区域设置。要分析语言字符串,可以使用Locale.forLanguageTag()
(自Java 7起提供):
final List acceptedLocales=new ArrayList();
最终字符串userLocale=request.getHeader(“接受语言”);
if(userLocale!=null){
最终列表范围=Locale.LanguageRange.parse(userLocale);
如果(范围!=null){
ranges.forEach(languageRange->{
最后一个字符串localeString=languageRange.getRange();
最终语言环境=Locale.forLanguageTag(localeString);
acceptedLocales.add(locale);
});
}
}
返回acceptedLocales;
我们正在使用Spring boot和Java 8。这很有效
在ApplicationConfig.java中编写
@Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
在我的constants类中有一个列表,它有我们支持的语言
List<Locale> locales = Arrays.asList(new Locale("en"),
new Locale("es"),
new Locale("fr"),
new Locale("es", "MX"),
new Locale("zh"),
new Locale("ja"));
List locales=Arrays.asList(新语言环境(“en”),
新区域设置(“es”),
新区域设置(“fr”),
新区域设置(“es”、“MX”),
新语言环境(“zh”),
新地区("ja);;
并在下面的课堂上写出逻辑
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse("da,es-MX;q=0.8");
Locale locale = Locale.lookup(ranges, locales);
return locale ;
}
}
公共类SmartLocaleResolver扩展了AcceptHeaderLocaleResolver{
@凌驾
公共区域设置resolveLocale(HttpServletRequest请求){
if(StringUtils.isBlank(request.getHeader(“接受语言”)){
返回Locale.getDefault();
}
List ranges=Locale.LanguageRange.parse(“da,es-MX;q=0.8”);
Locale=Locale.lookup(范围、区域设置);
返回区域设置;
}
}
上述解决方案缺乏某种验证。如果用户未提供有效的服务器区域设置,则使用ServletRequest.getLocale()
返回服务器区域设置
我们的网站最近收到各种接受语言的垃圾邮件请求,如:
secret.google.com
o-o-8-o-o.com搜索shell比谷歌好得多代码>
Google正式推荐o-o-8-o-o.com搜索shell代码>
Vitaly统治谷歌☆*:。゜゚・*ヽ(^ᴗ^)ノ*・゜゚。:*☆ ¯\_(ツ)_/¯(ಠ益ಠ)(ಥ‿ಥ)(ʘ‿ʘ)ლ(ಠ_ಠლ)( ͡° ͜ʖ ͡°)ヽ(゚Д゚)ノʕ•̫͡•ʔᶘ ᵒᴥᵒᶅ(=^^=)oO
此实现可以根据支持的有效区域设置列表进行可选检查。如果不进行此检查,使用“test”
或(2、3、4)的简单请求仍然会绕过LanguageRange.parse(String)
的语法验证
它允许空值和空值以允许搜索引擎爬虫
Servlet过滤器
final String headerAcceptLanguage = request.getHeader("Accept-Language");
// check valid
if (!HttpHeaderUtils.isHeaderAcceptLanguageValid(headerAcceptLanguage, true, Locale.getAvailableLocales()))
return;
实用程序
/**
* Checks if the given accept-language request header can be parsed.<br>
* <br>
* Optional the parsed LanguageRange's can be checked against the provided
* <code>locales</code> so that at least one locale must match.
*
* @see LanguageRange#parse(String)
*
* @param acceptLanguage
* @param isBlankValid Set to <code>true</code> if blank values are also
* valid
* @param locales Optional collection of valid Locale to validate any
* against.
*
* @return <code>true</code> if it can be parsed
*/
public static boolean isHeaderAcceptLanguageValid(final String acceptLanguage, final boolean isBlankValid,
final Locale[] locales)
{
// allow null or empty
if (StringUtils.isBlank(acceptLanguage))
return isBlankValid;
try
{
// check syntax
final List<LanguageRange> languageRanges = Locale.LanguageRange.parse(acceptLanguage);
// wrong syntax
if (languageRanges.isEmpty())
return false;
// no valid locale's to check against
if (ArrayUtils.isEmpty(locales))
return true;
// check if any valid locale exists
for (final LanguageRange languageRange : languageRanges)
{
final Locale locale = Locale.forLanguageTag(languageRange.getRange());
// validate available locale
if (ArrayUtils.contains(locales, locale))
return true;
}
return false;
}
catch (final Exception e)
{
return false;
}
}
其实并没有那么复杂:用逗号将冒号后面的部分分开,然后在每组中查找分号,然后解析语言代码和q因子。在将'-'
s替换为'
s之后,语言代码往往对应于java.util.Locale
s。您真的需要自己解析它吗能否使用[Http]ServletRequest.getLocale[s]让容器来处理复杂性?@bkail:请把你的评论放在一个答案中,因为它是“正确的”肯定的。这是否是一个servlet问题并不明显,尽管我猜java ee标记的存在表明OP使用servlet API可能会感到满意。除非你计划直接支持每一个可能的区域设置,否则请提供服务rRequest.getLocales可能是一个更好的选择。问题是ServletRequest。如果用户没有提供有效的服务器区域设置,getLocales
将返回服务器区域设置。要防止语言垃圾请求,您必须自己在LanguageRange.parse(字符串)中解析它
很方便。@djmj只要检查相关的Accept Language
头是否存在就足够简单了。不过,您是对的,较新的JDK添加了其他可能有用的API(这个答案是从2011年开始的!)。如果机器人程序滥用Accept Language
向您的网站发送垃圾邮件,则该网站存在,但没有有效的元素。如果某些google机器人对您的页面进行爬网,则“缺席”仍然有效。请参阅,如果您需要区域设置列表,可以使用Locale.LanguageRange.parse(requestedLangs).stream().sorted(Comparator.comparing(Locale.LanguageRange::getWeight).reversed()).map(range->new Locale(range.getRange()).collect(Collectors.toList());
@Alex:根据javadoc,您不需要对返回的列表进行排序:“与加权列表不同,按优先级排序的列表中的语言范围根据其优先级按降序排序。第一语言范围具有最高优先级,并且最符合用户的偏好。”。因此,您的代码可以是:Locale.LanguageRange.parse(requestedLangs).stream().map(range->new L
List<Locale> locales = Arrays.asList(new Locale("en"),
new Locale("es"),
new Locale("fr"),
new Locale("es", "MX"),
new Locale("zh"),
new Locale("ja"));
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse("da,es-MX;q=0.8");
Locale locale = Locale.lookup(ranges, locales);
return locale ;
}
}
final String headerAcceptLanguage = request.getHeader("Accept-Language");
// check valid
if (!HttpHeaderUtils.isHeaderAcceptLanguageValid(headerAcceptLanguage, true, Locale.getAvailableLocales()))
return;
/**
* Checks if the given accept-language request header can be parsed.<br>
* <br>
* Optional the parsed LanguageRange's can be checked against the provided
* <code>locales</code> so that at least one locale must match.
*
* @see LanguageRange#parse(String)
*
* @param acceptLanguage
* @param isBlankValid Set to <code>true</code> if blank values are also
* valid
* @param locales Optional collection of valid Locale to validate any
* against.
*
* @return <code>true</code> if it can be parsed
*/
public static boolean isHeaderAcceptLanguageValid(final String acceptLanguage, final boolean isBlankValid,
final Locale[] locales)
{
// allow null or empty
if (StringUtils.isBlank(acceptLanguage))
return isBlankValid;
try
{
// check syntax
final List<LanguageRange> languageRanges = Locale.LanguageRange.parse(acceptLanguage);
// wrong syntax
if (languageRanges.isEmpty())
return false;
// no valid locale's to check against
if (ArrayUtils.isEmpty(locales))
return true;
// check if any valid locale exists
for (final LanguageRange languageRange : languageRanges)
{
final Locale locale = Locale.forLanguageTag(languageRange.getRange());
// validate available locale
if (ArrayUtils.contains(locales, locale))
return true;
}
return false;
}
catch (final Exception e)
{
return false;
}
}