Есть ли способ использовать UTF-8 с движком приложения?
Я ищу некоторое объяснение того, как движок приложения имеет дело с кодировкой символов. Я работаю над клиент-серверным приложением, где сервер находится в приложении.
Это новое приложение, созданное с нуля, поэтому мы используем UTF-8 везде. Клиент отправляет некоторые строки на сервер через POST, x-www-form-urlencoded. Я получаю их и повторяю их. Когда клиент вернет его, он будет ISO-8859-1! Я также вижу это поведение, когда POSTing в blobstore, с параметрами, отправленными как UTF-8, multipart/form-data encoded.
Для записи, я вижу это в Wireshark. Поэтому я на 100% уверен, что отправляю UTF-8 и получаю ISO-8859-1. Кроме того, я не вижу mojibake: кодированные строки ISO-8859-1 прекрасно подходят. Это также не проблема неправильного толкования Content-Type. Это не клиент. Что-то по пути правильно распознает, что я отправляю параметры UTF-8, но по какой-то причине конвертирует их в ISO-8859-1.
Я убежден, что ISO-8859-1 является кодировкой символов по умолчанию для сервлетов GAE. Мой вопрос в том, есть ли способ сказать GAE не конвертировать в ISO-8859-1 и вместо этого использовать UTF-8 везде?
Скажем, сервлет делает что-то вроде этого:
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("application/json");
String name = req.getParameter("name");
String json = "{\"name\":\"" + name + "\"}";
resp.getOutputStream().print(json);
}
Я попытался настроить кодировку символов ответа и запрос на "UTF-8", но это ничего не изменило.
Спасибо заранее,
Ответы
Ответ 1
Найден способ обойти его. Вот как я это сделал:
-
В качестве типа содержимого используется "application/json; charset = UTF-8". В качестве альтернативы, установите кодировку ответов на "UTF-8" (либо будет работать нормально, не нужно делать оба).
-
Base64 кодирует входные строки, которые не являются ASCII-безопасными, и поступают как UTF-8. В противном случае они, очевидно, преобразуются в ISO-8859-1, когда они попадают в сервлет.
-
Используется resp.getWriter() вместо resp.getOutputStream() для печати ответа JSON.
После all эти условия были выполнены, я, наконец, смог вывести UTF-8 обратно клиенту.
Ответ 2
Я вижу две вещи, которые вы должны сделать.
1) установить системные свойства (если вы используете его) в utf8 в appengine-web.xml
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
<property name="file.encoding" value="UTF-8" />
<property name="DEFAULT_ENCODING" value="UTF-8" />
</system-properties>
ОК, что выше, это то, что у меня есть, но документы предлагают это ниже:
<env-variables>
<env-var name="DEFAULT_ENCODING" value="UTF-8" />
</env-variables>
https://developers.google.com/appengine/docs/java/config/appconfig
2) укажите кодировку, когда вы устанавливаете тип содержимого, или она вернется к стандартным
Тип содержимого может включать в себя тип кодировки символов, используемый для пример, text/html; кодировка = ISO-8859-4.
Я бы попробовал
resp.setContentType("application/json; charset=UTF-8");
Вы также можете попробовать сценарий, который позволяет напрямую установить для него тип содержимого.
http://docs.oracle.com/javaee/1.3/api/javax/servlet/ServletResponse.html#getWriter%28%29
http://docs.oracle.com/javaee/1.3/api/javax/servlet/ServletResponse.html#setContentType(java.lang.String)
Для чего это стоит, мне нужно utf8 для японского контента, и у меня нет проблем. В любом случае, я не использую фильтр или setContentType. Я использую gwt и # 1 выше, и он работает.
Ответ 3
Это не относится к GAE, но если вы сочтете это полезным: я создал свой собственный фильтр:
В web.xml
<filter>
<filter-name>charsetencoding</filter-name>
<filter-class>mypackage.CharsetEncodingFilter</filter-class>
</filter>
...
<filter-mapping>
<filter-name>charsetencoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(поместите фрагмент, сопоставляющий фильтр, в начале сопоставлений фильтра и проверьте свой шаблон url.
И
public class CharsetEncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
req.setCharacterEncoding("UTF-8");
chain.doFilter(req, res);
res.setCharacterEncoding("UTF-8");
}
public void destroy() { }
public void init(FilterConfig filterConfig) throws ServletException { }
}
Ответ 4
Обходной путь (безопасный)
Ничто из этих ответов не сработало для меня, поэтому я написал этот класс кодировать UTF-строки в ASCII-Strings (заменяя все символы, отсутствующие в таблице ASCII, с их табличным номером, предшествующий и сопровождаемый знаком), используя AsciiEncoder.encode(yourString)
Строка может быть декодирована обратно в UTF с помощью AsciiEncoder.decode(yourAsciiEncodedString)
.
package <your_package>;
import java.util.ArrayList;
/**
* Created by Micha F. aka Peracutor.
* 04.06.2017
*/
public class AsciiEncoder {
public static final char MARK = '%'; //use whatever ASCII-char you like (should be occurring not often in regular text)
public static String encode(String s) {
StringBuilder result = new StringBuilder(s.length() + 4 * 10); //buffer for 10 special characters (4 additional chars for every special char that gets replaced)
for (char c : s.toCharArray()) {
if ((int) c > 127 || c == MARK) {
result.append(MARK).append((int) c).append(MARK);
} else {
result.append(c);
}
}
return result.toString();
}
public static String decode(String s) {
int lastMark = -1;
ArrayList<Character> chars = new ArrayList<>();
try {
//noinspection InfiniteLoopStatement
while (true) {
String charString = s.substring(lastMark = s.indexOf(MARK, lastMark + 1) + 1, lastMark = s.indexOf(MARK, lastMark));
char c = (char) Integer.parseInt(charString);
chars.add(c);
}
} catch (IndexOutOfBoundsException | NumberFormatException ignored) {}
for (char c : chars) {
s = s.replace("" + MARK + ((int) c) + MARK, String.valueOf(c));
}
return s;
}
}
Надеюсь, это поможет кому-то.