Quién establece el tipo de contenido de respuesta en Spring MVC (@ResponseBody)

Tengo en mi aplicación web Spring MVC Java accionada por Annotation ejecutada en el servidor web jetty (actualmente en maven jetty plugin).

Estoy tratando de hacer algo de compatibilidad con AJAX con un método de controlador que solo devuelve el texto de ayuda de Cadena. Los recursos están en encoding UTF-8 y también la cadena, pero mi respuesta del servidor viene con

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

incluso cuando mi navegador envía

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

Estoy usando de alguna manera la configuración predeterminada de la spring

He encontrado una pista para agregar este bean a la configuración, pero creo que simplemente no se usa, porque dice que no es compatible con la encoding y en su lugar se usa una predeterminada.

    

Mi código de controlador es (tenga en cuenta que este cambio de tipo de respuesta no funciona para mí):

 @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; } 

La statement simple del bean StringHttpMessageConverter no es suficiente, necesita StringHttpMessageConverter en AnnotationMethodHandlerAdapter :

          

Sin embargo, con este método debe redefinir todos los HttpMessageConverter y tampoco funciona con .

Entonces, quizás el método más conveniente pero feo es interceptar la BeanPostProcessor de instancias de AnnotationMethodHandlerAdapter con 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; } } 

  

Encontré una solución para Spring 3.1. con el uso de la anotación @ResponseBody. Aquí hay un ejemplo de controlador usando salida Json:

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

Tenga en cuenta que en Spring MVC 3.1 puede usar el espacio de nombres MVC para configurar los convertidores de mensajes:

        

O configuración basada en código:

 @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { private static final Charset UTF8 = Charset.forName("UTF-8"); @Override public void configureMessageConverters(List> converters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "plain", UTF8))); converters.add(stringConverter); // Add other converters ... } } 

Solo en caso de que también pueda establecer la encoding de la siguiente manera:

 @RequestMapping(value = "ajax/gethelp") public ResponseEntity 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("returning: " + help, responseHeaders, HttpStatus.CREATED); } 

Creo que usar StringHttpMessageConverter es mejor que esto.

puede agregar produce = “text / plain; charset = UTF-8” a 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); } 

mira este blog para más detalles

Estuve luchando contra este problema recientemente y encontré una respuesta mucho mejor disponible en Spring 3.1:

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

Entonces, tan fácil como JAX-RS al igual que todos los comentarios indicaron que podría / debería ser.

Establecí el tipo de contenido en MarshallingView en el bean ContentNegotiatingViewResolver . Funciona de manera fácil, limpia y sin problemas:

           

Puede usar produce para indicar el tipo de respuesta que está enviando desde el controlador. Esta palabra clave “produce” será más útil en la solicitud de ajax y fue muy útil en mi proyecto

 @RequestMapping(value = "/aURLMapping.htm", method = RequestMethod.GET, produces = "text/html; charset=utf-8") public @ResponseBody String getMobileData() { } 

Estoy usando CharacterEncodingFilter, configurado en web.xml. Tal vez eso ayude.

   characterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter  encoding UTF-8   forceEncoding true   

Gracias digz6666, tu solución funciona para mí con ligeros cambios porque estoy usando json:

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

La respuesta dada por axtavt (que has recomendado) no funcionará para mí. Incluso si agregué el tipo de medio correcto:

 if (instancia de conversión de StringHttpMessageConverter) {                   
                     ((StringHttpMessageConverter) conv) .setSupportedMediaTypes (
                         Arrays.asList (
                                 nuevo MediaType ("text", "html", Charset.forName ("UTF-8")),
                                 nuevo MediaType ("aplicación", "json", Charset.forName ("UTF-8"))));
                 }

si ninguno de los anteriores funcionó para usted, intente hacer que las peticiones ajax en “POST” no “GET”, eso funcionó bien para mí … ninguna de las anteriores sí lo hizo. También tengo el characterEncodingFilter.

 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 restredParamToOriginal; /** * @param wrongUrlParam * @return Repaird 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 */ restredParamToOriginal = URLEncoder.encode(wrongUrlParam, "ISO-8859-1"); /* Second step: decode to UTF-8 again from the original one */ return URLDecoder.decode(restredParamToOriginal, "UTF-8"); } } 

Después de haber intentado solucionar este problema … lo pensé y funciona bien.

La forma más simple de resolver este problema en Spring 3.1.1 es que: agregue los siguientes códigos de configuración en servlet-context.xml

      text/plain;charset=UTF-8     

No es necesario anular o implementar nada.

De acuerdo con el enlace “Si no se especifica una encoding de caracteres, la especificación del servlet requiere que se utilice una encoding de ISO-8859-1” .Si está utilizando el resorte 3.1 o posterior, use la siguiente configuración para configurar charset = UTF-8 en cuerpo de respuesta
@RequestMapping (value = “su url de mapeo”, produce = “text / plain; charset = UTF-8”)

si decide solucionar este problema a través de la siguiente configuración:

        

debe confirmar que solo debe haber una etiqueta mvc: anotada en todo su archivo * .xml. de lo contrario, la configuración puede no ser efectiva.

 public final class ConfigurableStringHttpMessageConverter extends AbstractHttpMessageConverter { private Charset defaultCharset; public Charset getDefaultCharset() { return defaultCharset; } private final List 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.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.availableCharsets().values()); } /** * Indicates whether the {@code Accept-Charset} should be written to any outgoing request. * 

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}. * *

By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses. * * @return the list of accepted charsets */ protected List getAcceptedCharsets() { return this.availableCharsets; } private Charset getContentTypeCharset(MediaType contentType) { if (contentType != null && contentType.getCharSet() != null) { return contentType.getCharSet(); } else { return defaultCharset; } } }

Configuración de muestra: