- PVSM.RU - https://www.pvsm.ru -
Для передачи данных от интерфейса веб-приложения есть несколько методов, но, пожалуй, самый распространенный — отправка формы с MIME-type application/x-www-form-urlencoded
. Ещё один вариант — multipart/form-data
.
В качестве серверной технологии для приема могут использоваться контроллеры в MVC-фреймворках (из основных Java-технологий следует упомянуть Spring MVC [1], Java Server Pages [2], Java Server Faces [3]. Но эти фреймворки довольно сильно осложняют жизнь разработчику интерфейса, если он не знаком с Java или нужен шаг в сторону от того, что позволяет фреймворк. В случае же экспонирования REST-интерфейса бэкендом приложения разработкой фронта упрощается: ей может заниматься человек, знающий базовый javascript и jquery, независимый от разработки бэка. Кроме того, даже при использовании шаблонизатора, выбор сильно расширяется: Apache Velocity [4], FreeMarker [5] (стоит упомянуть, что Spirng MVC неплохо интегрируется с последними). Тогда данные из формы на стороне сервера записываются в бины, с которыми связан данный view/controller. Правда, у JSF тоже наблюдалась генетическая проблема с кодировками, рассмотрение которой — тема для отдельной статьи.
Краткое введение в JAX-RS было дано в предыдущей статье [6]. И экспонированный через JAX-RS интерфейс может принимать GET
и POST
запросы с данными форм. О проблемах данного подхода при использовании не-latin-1
и пойдет речь в данной статье.
Для инжекции парамтра из формы в метод существует несколько аннотаций: @Form
, @FormParam
. Для POST
-запроса @FormParam
эквивалентен @QueryParam
для GET
по поведению. С маленьким исключением: поведение при использовании utf-8. В случае использования метода GET
декодирование в строку из urlencoded
происходит без проблем. Для POST
-же в Content-Type
требуется указать charset=utf-8
, чтобы при декодировании из urlencoded
преобразование байтового потока происходило в UTF-8, а не Latin-1 (поведение по умолчанию).
Браузеры (или jquery, например) не указывают кодировку, что приводит к неправильной интерпретации значений полей формы, если они содержат старшие CP UTF-8. Обходится данная проблема при использовании jquery явным указанием заголовка Content-Type: application/x-www-form-urlencoded; charset=utf-8
. Для формы без js мне не удалось достичь успеха указанием enctype для формы.
Возникает, конечно, вопрос: надо ли использовать аннотацию @Form, если данные будут приходить и от других приложений? В нашем случае её использование оправдано из принципов DRY: один и тот же метод может использоваться и формой клиента, и внешними приложениями, использующими это API.
При использовании клиентского фрейморка Resteasy возможны несколько вариантов. Например, можно добавить в необходимые методы @HeaderParam("Content-Type")
:
@Path("/")
public interface Rest {
@POST
@Path("test")
public String test(@FormParam("q") String query, @HeaderParam("Content-Type") String contentType);
}
Использование тогда вышлядит следующим образом:
Rest client = ProxyFactory.create(Rest.class, url);
client.test(query, "application/x-www-form-urlencode; charset=utf-8");
Но более правильным и удобным будет использование клиентского интерсептора, добавляющего в заголовок Content-Type: application/x-www-form-urlencoded
поле charset
. Реализуется это следующим образом:
@ClientInterceptor
@HeaderDecoratorPrecedence
public class RestInterceptor implements ClientExecutionInterceptor {
public static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
public static final String FORM_CONTENT_TYPE_WITH_CHARSET = "application/x-www-form-urlencoded; charset=utf-8";
@Override
public ClientResponse execute(ClientExecutionContext context) throws Exception {
String contentType = context.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
if (formWithoutCharset(contentType)) {
context.getRequest().header(HttpHeaders.CONTENT_TYPE, FORM_CONTENT_TYPE_WITH_CHARSET);
}
return context.proceed();
}
private boolean formWithoutCharset(String contentType) {
return contentType != null
&& contentType.contains(FORM_CONTENT_TYPE)
&& ! contentType.contains("charset");
}
}
Такой вариант интерсептора, конечно, не идеален. Возможно, что действительно хочется отправить форму в latin-1
… Но это первый шаг к тому, чтобы упростить код клиента.
Для его активации необходимо немного поправить процедуру инициализации фреймворка:
public static void initResteasy() {
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(factory);
InterceptorRegistry<ClientExecutionInterceptor> registry = factory.getClientExecutionInterceptorRegistry();
registry.register(new RestInterceptor());
}
После этого и при использовании ClientRequest
, и при использовании прокси-объектов в процессе отправки запроса при наличии заголовка Content-Type
с application/x-www-form-urlencoded
, но без charset
, то проставляется заголовок, его содержащий.
Автор: grossws
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/3838
Ссылки в тексте:
[1] Spring MVC: http://www.springsource.org/spring-framework
[2] Java Server Pages: http://jcp.org/en/jsr/detail?id=245
[3] Java Server Faces: http://www.jcp.org/en/jsr/detail?id=344
[4] Apache Velocity: http://velocity.apache.org/
[5] FreeMarker: http://freemarker.sourceforge.net/
[6] предыдущей статье: http://habrahabr.ru/post/140181/
Нажмите здесь для печати.