2010-09-01 2 views
111

У меня есть мое приложение, связанное с аннотацией Spring MVC Java, запущенное на причальном веб-сервере (в настоящее время в плагине для maven).Кто устанавливает тип содержимого ответа в Spring MVC (@ResponseBody)

Я пытаюсь выполнить некоторую поддержку AJAX с помощью одного метода контроллера, возвращающего только текст справки строки. Ресурсы в UTF-8 кодировке и поэтому строка, но мой ответ от сервера приходит с

content-encoding: text/plain;charset=ISO-8859-1 

даже когда мой браузер посылает

Accept-Charset windows-1250,utf-8;q=0.7,*;q=0.7 

Я использую как-то по умолчанию конфигурации пружины

Я нашел подсказку, чтобы добавить этот компонент в конфигурацию, но я думаю, что он просто не используется, потому что он говорит, что он не поддерживает кодировку, а используется по умолчанию.

<bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
    <property name="supportedMediaTypes" value="text/plain;charset=UTF-8" /> 
</bean> 

Мой код контроллера (обратите внимание, что это изменение типа ответа не работает для меня):

@RequestMapping(value = "ajax/gethelp") 
public @ResponseBody String handleGetHelp(Locale loc, String code, HttpServletResponse response) { 
    log.debug("Getting help for code: " + code); 
    response.setContentType("text/plain;charset=UTF-8"); 
    String help = messageSource.getMessage(code, null, loc); 
    log.debug("Help is: " + help); 
    return help; 
} 

ответ

56

Простая декларация StringHttpMessageConverter боба не хватает, вам нужно, чтобы ввести его в AnnotationMethodHandlerAdapter:

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
    <property name="messageConverters"> 
     <array> 
      <bean class = "org.springframework.http.converter.StringHttpMessageConverter"> 
       <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> 
      </bean> 
     </array> 
    </property> 
</bean> 

Однако, используя этот метод, вы должны переопределить все HttpMessageConverter с, а также Безразлично» t с <mvc:annotation-driven />.

Так, пожалуй, самый удобный, но некрасиво способ перехватить создание экземпляра AnnotationMethodHandlerAdapter с BeanPostProcessor:

public class EncodingPostProcessor implements BeanPostProcessor { 
    public Object postProcessBeforeInitialization(Object bean, String name) 
      throws BeansException { 
     if (bean instanceof AnnotationMethodHandlerAdapter) { 
      HttpMessageConverter<?>[] convs = ((AnnotationMethodHandlerAdapter) bean).getMessageConverters(); 
      for (HttpMessageConverter<?> conv: convs) { 
       if (conv instanceof StringHttpMessageConverter) { 
        ((StringHttpMessageConverter) conv).setSupportedMediaTypes(
         Arrays.asList(new MediaType("text", "html", 
          Charset.forName("UTF-8")))); 
       } 
      } 
     } 
     return bean; 
    } 

    public Object postProcessAfterInitialization(Object bean, String name) 
      throws BeansException { 
     return bean; 
    } 
} 

-

<bean class = "EncodingPostProcessor " /> 
+0

Работает как шарм, большое спасибо – Hurda

+10

Это похоже на грязный хак. Мне это не нравится, но нужно использовать. Разработчики Spring Framework должны работать над этим делом! – digz6666

+0

Где находится строка go? – zod

3

Я использую CharacterEncodingFilter, сконфигурированный в web.xml. Может быть, это помогает.

<filter> 
    <filter-name>characterEncodingFilter</filter-name> 
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
    <init-param> 
     <param-name>encoding</param-name> 
     <param-value>UTF-8</param-value> 
    </init-param> 
    <init-param> 
     <param-name>forceEncoding</param-name> 
     <param-value>true</param-value> 
    </init-param> 
</filter> 
+0

Это просто фильтрует символ в запросе, а не в ответ - я уже использую этот – Hurda

+0

@Hurda: С 'forceEncoding = true' он также фильтрует ответ, но в этом случае это не поможет. – axtavt

+0

Лучший и быстрый ответ.Я также уже декларировал и использовал этот фильтр, но с '' 'forceEncoding = false'''. Я просто установил его в '' '' false''' и 'charset = UTF-8' успешно добавлен в '' 'Content-Type'''. –

41

Только в случае, если вы также можете установить кодировку следующим образом :

@RequestMapping(value = "ajax/gethelp") 
public ResponseEntity<String> handleGetHelp(Locale loc, String code, HttpServletResponse response) { 
    HttpHeaders responseHeaders = new HttpHeaders(); 
    responseHeaders.add("Content-Type", "text/html; charset=utf-8"); 

    log.debug("Getting help for code: " + code); 
    String help = messageSource.getMessage(code, null, loc); 
    log.debug("Help is: " + help); 

    return new ResponseEntity<String>("returning: " + help, responseHeaders, HttpStatus.CREATED); 
} 

Я думаю, используя StringHttpMes sageConverter лучше этого.

+9

+1 ... так раздражает, что весна делает это так тяжело. Это так просто в JAX-RS. –

+0

Ты спас меня, парень! – Igorock

+2

+10 internets, это было единственное, что на самом деле работало для меня –

2

Благодаря digz6666, ваше решение работает для меня с небольшими изменениями, потому что я использую JSON:

 
responseHeaders.add("Content-Type", "application/json; charset=utf-8"); 

Ответ дается axtavt (whch вы рекомендованное) не будет работать для меня. Даже если я добавил правильный тип носителя:

 
if (conv instanceof StringHttpMessageConverter) {     
        ((StringHttpMessageConverter) conv).setSupportedMediaTypes(
         Arrays.asList(
           new MediaType("text", "html", Charset.forName("UTF-8")), 
           new MediaType("application", "json", Charset.forName("UTF-8")))); 
       } 
2

, если ни один из вышеперечисленных работал для вас не пытаются сделать AJAX запросы на «POST» не «GET», которые работали для меня хорошо ... ни один из выше было. У меня также есть characterEncodingFilter.

2
package com.your.package.spring.fix; 

import java.io.UnsupportedEncodingException; 
import java.net.URLDecoder; 
import java.net.URLEncoder; 

/** 
* @author Szilard_Jakab (JaKi) 
* Workaround for Spring 3 @ResponseBody issue - get incorrectly 
    encoded parameters  from the URL (in example @ JSON response) 
* Tested @ Spring 3.0.4 
*/ 
public class RepairWrongUrlParamEncoding { 
    private static String restoredParamToOriginal; 

    /** 
    * @param wrongUrlParam 
    * @return Repaired url param (UTF-8 encoded) 
    * @throws UnsupportedEncodingException 
    */ 
    public static String repair(String wrongUrlParam) throws 
              UnsupportedEncodingException { 
    /* First step: encode the incorrectly converted UTF-8 strings back to 
        the original URL format 
    */ 
    restoredParamToOriginal = URLEncoder.encode(wrongUrlParam, "ISO-8859-1"); 

    /* Second step: decode to UTF-8 again from the original one 
    */ 
    return URLDecoder.decode(restoredParamToOriginal, "UTF-8"); 
    } 
} 

После того, как я пробовал много обходного пути для этой проблемы .. Я подумал об этом, и он отлично работает.

48

Обратите внимание, что в Spring MVC 3.1 вы можете использовать пространство имен MVC настроить конвертеры сообщение:

<mvc:annotation-driven> 
    <mvc:message-converters register-defaults="true"> 
    <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
     <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> 
    </bean> 
    </mvc:message-converters> 
</mvc:annotation-driven> 

Или код на основе конфигурации:

@Configuration 
@EnableWebMvc 
public class WebConfig extends WebMvcConfigurerAdapter { 

    private static final Charset UTF8 = Charset.forName("UTF-8"); 

    @Override 
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); 
    stringConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", UTF8))); 
    converters.add(stringConverter); 

    // Add other converters ... 
    } 
} 
+0

Сортировка работ, за исключением того, что 1) он загрязняет ответ заголовком «Accept-Charset», который, вероятно, перечисляет все известные кодировки символов, и 2) [когда запрос имеет заголовок 'Accept'' свойство 'supportedMediaTypes' преобразователя не используется] (http://forum.spring.io/forum/spring-projects/web/108536-responsebody-reports-utf-8-charset-in-http-response-but-uses-actually-iso-8859 -1-ha), поэтому, например, когда я делаю запрос, набрав непосредственно URL-адрес в браузере, вместо ответа вместо него будет заголовок 'Content-Type: text/html'. –

+2

Вы можете упростить, так как «text/plain» по умолчанию равно: '' –

+0

Этот ответ должны быть приняты в качестве правильного ответа. Кроме того, метод @IgorMukhin для определения компонента StringHttpMessageConverter работает. Этот ответ используется для установки типов содержимого ответов для всех сервлетов. Если вам просто нужно задать тип содержимого ответа для конкретного метода контроллера, используйте вместо него ответ «Воин» (использование вызывает аргумент в @RequestMapping) – PickBoy

4

Я установил тип содержимого в MarshallingView в блоке ContentNegotiatingViewResolver. Он работает легко, чисто и гладко:

<property name="defaultViews"> 
    <list> 
    <bean class="org.springframework.web.servlet.view.xml.MarshallingView"> 
     <constructor-arg> 
     <bean class="org.springframework.oxm.xstream.XStreamMarshaller" />  
     </constructor-arg> 
     <property name="contentType" value="application/xml;charset=UTF-8" /> 
    </bean> 
    </list> 
</property> 
10

я боролся этот вопрос в последнее время и нашел много лучшего ответа доступны в Spring 3.1:

@RequestMapping(value = "ajax/gethelp", produces = "text/plain") 

Так что, как легко, как JAX-RS точно так же как все комментарии указывают, что он может/должен быть.

+0

Стоит портировать до весны 3.1 для! –

+4

@dbyoung Это не похоже на то, что javadoc для 'производит' говорит:« ... запрос отображается только в том случае, если Content-Type соответствует одному из этих типов медиа ». это означает, что AFAIK, что 'производит', имеет отношение к тому, соответствует ли метод запросу, а не как какой тип содержимого должен иметь ответ. – Ittai

+0

@Ittai правильный! «производит» определяет, соответствует ли метод запросу, но НЕ какой тип содержимого находится в ответе. что-то другое должно смотреть на «производит» при определении того, какой тип контента установить – anton1980

140

Я нашел решение для весны 3.1. с использованием аннотации @ResponseBody. Вот пример контроллера с использованием JSon выхода:

@RequestMapping(value = "/getDealers", method = RequestMethod.GET, 
produces = "application/json; charset=utf-8") 
@ResponseBody 
public String sendMobileData() { 

} 
+6

+1. Это также решило это для меня, но только после того, как я переключился на использование '' в applicationContext. (Вместо '', который устарел весной 3.2 в любом случае ...) – Jonik

+0

Как это сделать приложение/xml, если аннотировать этот путь? – Hurda

+2

@Hurda: Очевидно, вы можете указать любой тип контента, который вы хотите, изменив значение атрибута 'производит'. – Jonik

20

вы можете добавить продуцирует = "текст/равнинный, кодировка = UTF-8" в RequestMapping

@RequestMapping(value = "/rest/create/document", produces = "text/plain;charset=UTF-8") 
@ResponseBody 
public String create(Document document, HttpServletRespone respone) throws UnsupportedEncodingException { 

    Document newDocument = DocumentService.create(Document); 

    return jsonSerializer.serialize(newDocument); 
} 

see this blog for more detail

+2

Этот код не будет компилироваться; вы возвращаете что-то из метода void. –

+2

Извините, плохая ошибка, теперь исправлено –

+2

Это неправильный ответ. В соответствии с весенними документами: Воспроизводимые типы носителей запрошенного запроса, сужающие первичное сопоставление. Формат представляет собой последовательность типов носителей («текст/обычная», «приложение/*), с запросом, отображаемым только в том случае, если Accept соответствует одному из этих типов носителей. Выражения можно скрыть с помощью оператора«! », как в «! text/plain», который соответствует всем запросам с помощью Accept, отличного от «text/plain». –

0
public final class ConfigurableStringHttpMessageConverter extends AbstractHttpMessageConverter<String> { 

    private Charset defaultCharset; 

    public Charset getDefaultCharset() { 
     return defaultCharset; 
    } 

    private final List<Charset> availableCharsets; 

    private boolean writeAcceptCharset = true; 

    public ConfigurableStringHttpMessageConverter() { 
     super(new MediaType("text", "plain", StringHttpMessageConverter.DEFAULT_CHARSET), MediaType.ALL); 
     defaultCharset = StringHttpMessageConverter.DEFAULT_CHARSET; 
     this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values()); 
    } 

    public ConfigurableStringHttpMessageConverter(String charsetName) { 
     super(new MediaType("text", "plain", Charset.forName(charsetName)), MediaType.ALL); 
     defaultCharset = Charset.forName(charsetName); 
     this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values()); 
    } 

    /** 
    * Indicates whether the {@code Accept-Charset} should be written to any outgoing request. 
    * <p>Default is {@code true}. 
    */ 
    public void setWriteAcceptCharset(boolean writeAcceptCharset) { 
     this.writeAcceptCharset = writeAcceptCharset; 
    } 

    @Override 
    public boolean supports(Class<?> clazz) { 
     return String.class.equals(clazz); 
    } 

    @Override 
    protected String readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException { 
     Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType()); 
     return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset)); 
    } 

    @Override 
    protected Long getContentLength(String s, MediaType contentType) { 
     Charset charset = getContentTypeCharset(contentType); 
     try { 
      return (long) s.getBytes(charset.name()).length; 
     } 
     catch (UnsupportedEncodingException ex) { 
      // should not occur 
      throw new InternalError(ex.getMessage()); 
     } 
    } 

    @Override 
    protected void writeInternal(String s, HttpOutputMessage outputMessage) throws IOException { 
     if (writeAcceptCharset) { 
      outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets()); 
     } 
     Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType()); 
     FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset)); 
    } 

    /** 
    * Return the list of supported {@link Charset}. 
    * 
    * <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses. 
    * 
    * @return the list of accepted charsets 
    */ 
    protected List<Charset> getAcceptedCharsets() { 
     return this.availableCharsets; 
    } 

    private Charset getContentTypeCharset(MediaType contentType) { 
     if (contentType != null && contentType.getCharSet() != null) { 
      return contentType.getCharSet(); 
     } 
     else { 
      return defaultCharset; 
     } 
    } 
} 

Конфигурация образца:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
     <property name="messageConverters"> 
      <util:list> 
       <bean class="ru.dz.mvk.util.ConfigurableStringHttpMessageConverter"> 
        <constructor-arg index="0" value="UTF-8"/> 
       </bean> 
      </util:list> 
     </property> 
    </bean> 
3

Вы можете использовать функции получения, чтобы указать тип ответа, отправляемого с контроллера. Это «производит» ключевое слово будет наиболее полезным в запросе Ajax и был очень полезным в моем проекте

@RequestMapping(value = "/aURLMapping.htm", method = RequestMethod.GET, produces = "text/html; charset=utf-8") 

public @ResponseBody String getMobileData() { 

} 
2

Простой способ решить эту проблему в Spring 3.1.1 является то, что: добавить следующие коды конфигурации в servlet-context.xml

<annotation-driven> 
    <message-converters register-defaults="true"> 
    <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
    <beans:property name="supportedMediaTypes">  
    <beans:value>text/plain;charset=UTF-8</beans:value> 
    </beans:property> 
    </beans:bean> 
    </message-converters> 
    </annotation-driven> 

Не нужно ничего переопределять или внедрять.

1

В соответствии с link «Если кодировка символов не указана, спецификация Servlet требует использования кодировки ISO-8859-1». Если вы используете Spring 3.1 или более позднюю версию, используйте конфигурацию для установки charset = UTF-8 в тело ответа
@RequestMapping (значение = "ваше отображение URL", производит = "текст/равнинная, кодировка = UTF-8")

1

если вы решили исправить эту проблему путем следующая конфигурация:

<mvc:annotation-driven> 
    <mvc:message-converters register-defaults="true"> 
    <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
     <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> 
    </bean> 
    </mvc:message-converters> 
</mvc:annotation-driven> 

вы должны подтвердить, что во всем вашем файле * .xml должен быть только один тег mvc: annotation. иначе конфигурация может быть неэффективной.