Кодировка символа Джерси/остатка по умолчанию - программирование
Подтвердить что ты не робот

Кодировка символа Джерси/остатка по умолчанию

Джерси, кажется, терпит неудачу при возвращении 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...

4b9b3361

Ответ 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);
            }
        }
    }
}