Кодировка символа Джерси/остатка по умолчанию
Джерси, кажется, терпит неудачу при возвращении JSON...
Это:
@GET
@Produces( MediaType.APPLICATION_JSON + ";charset=UTF-8")
public List<MyObject> getMyObjects() {
return ....;
}
необходим, чтобы вернуть кодировку JSON utf-8. Если я использую только
@Produces( MediaType.APPLICATION_JSON)
не удастся, и, например, немецкий умлаут (üöä), будет возвращен неправильно.
Два вопроса:
1 - Для стандарта JSON utf-8 стандартно - почему бы не с Джерси?
2 - Могу ли я установить utf-8 для всего REST-сервлета, если приходит запрос JSON?
Я использую Jersey 1.5 и CRest 1.0.1 на Android...
Ответы
Ответ 1
Предложение SRG работает как шарм. Тем не менее, с Джерси 2.0 интерфейсы немного отличаются, поэтому нам пришлось немного адаптировать фильтр:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
public class CharsetResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) {
MediaType type = response.getMediaType();
if (type != null) {
String contentType = type.toString();
if (!contentType.contains("charset")) {
contentType = contentType + ";charset=utf-8";
response.getHeaders().putSingle("Content-Type", contentType);
}
}
}
}
Ответ 2
У меня была та же проблема: мне не нравится добавлять кодировку в тег @Produces везде.
Я нашел решение прямо здесь: http://stephen.genoprime.com/2011/05/29/jersey-charset-in-content-type.html
В принципе, вам просто нужно добавить фильтр ответов, который добавит кодировку (например, если возвращаемый тип содержимого - это текст, xml или json)
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
public class CharsetResponseFilter implements ContainerResponseFilter {
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
MediaType contentType = response.getMediaType();
response.getHttpHeaders().putSingle("Content-Type", contentType.toString() + ";charset=UTF-8");
return response;
}
}
И зарегистрировать фильтр:
ServletAdapter jerseyAdapter = new ServletAdapter();
jerseyAdapter.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters", "com.my.package.MyResponseFilter");
Также работает с Guice, конечно, например, в вашем классе, расширяющем ServletModule:
final Map<String, String> parameters = new HashMap<String, String>();
parameters.put("com.sun.jersey.spi.container.ContainerResponseFilters", com.package.JerseyCharsetResponseFilter.class.getName());
serve("/*").with(GuiceContainer.class, parameters);
Ответ 3
SRG и решение martins хорошо работали для меня.
Однако мне пришлось применить следующие изменения к фильтру:
Если клиент выполняет запрос с заголовком Accept, Джерси добавляет фактор качества к типу контента. Это выглядит следующим образом:
Нет проблем: запрос без заголовка Accept:
curl -i http://www.example.com/my-rest-endpoint
response.getMediaType().toString()
application/json
. Мы можем просто добавить ;charset=utf-8
.
Проблема: запрос с заголовком Accept:
curl -i -H "Accept: application/json" http://www.example.com/my-rest-endpoint
response.getMediaType().toString()
составляет {application/json, q=1000}
. Мы не можем просто добавить ;charset=utf-8
, так как это приведет к следующему исключению:
java.lang.IllegalArgumentException: Error parsing media type '{application/json, q=1000};charset=utf-8'
at org.glassfish.jersey.message.internal.MediaTypeProvider.fromString(MediaTypeProvider.java:92) ~[na:na]
at org.glassfish.jersey.message.internal.MediaTypeProvider.fromString(MediaTypeProvider.java:60) ~[na:na]
at javax.ws.rs.core.MediaType.valueOf(MediaType.java:179) ~[na:na]
...
Caused by: java.text.ParseException: Next event is not a Token
at org.glassfish.jersey.message.internal.HttpHeaderReader.nextToken(HttpHeaderReader.java:129) ~[na:na]
at org.glassfish.jersey.message.internal.MediaTypeProvider.valueOf(MediaTypeProvider.java:110) ~[na:na]
at org.glassfish.jersey.message.internal.MediaTypeProvider.fromString(MediaTypeProvider.java:90) ~[na:na]
... 193 common frames omitted
Я бы предложил следующий код для решения этой проблемы:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
public class CharsetResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) {
MediaType type = response.getMediaType();
if (type != null) {
if (!type.getParameters().containsKey(MediaType.CHARSET_PARAMETER)) {
MediaType typeWithCharset = type.withCharset("utf-8");
response.getHeaders().putSingle("Content-Type", typeWithCharset);
}
}
}
}