Spring MVC'de yanıt içerik türünü kim belirler (@ResponseBody)


126

Annotation güdümlü Spring MVC Java web uygulamamda iskele web sunucusunda (şu anda maven jetty eklentisinde) çalıştırıyorum.

Yalnızca String yardım metnini döndüren bir denetleyici yöntemi ile bazı AJAX desteği yapmaya çalışıyorum. Kaynaklar UTF-8 kodlamasındadır ve dize de öyle, ancak sunucudan aldığım yanıt

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

tarayıcım gönderdiğinde bile

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

Bir şekilde varsayılan yay yapılandırmasını kullanıyorum

Bu fasulyeyi yapılandırmaya eklemek için bir ipucu buldum, ancak kodlamayı desteklemediğini ve bunun yerine varsayılan bir tanesinin kullanıldığını söylediği için kullanılmadığını düşünüyorum.

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

Denetleyici kodum (bu yanıt türü değişikliğinin benim için çalışmadığını unutmayın):

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

Yanıtlar:


59

StringHttpMessageConverterFasulyenin basit beyanı yeterli değil, şunlara enjekte etmeniz gerekiyor 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>

Ancak, bu yöntemi kullanarak tüm e'leri yeniden tanımlamanız gerekir HttpMessageConverterve aynı zamanda çalışmaz <mvc:annotation-driven />.

Yani belki de en uygun ama çirkin yöntem kesişme örnekleme etmektir AnnotationMethodHandlerAdapterile 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 " />

10
Kirli bir hack gibi görünüyor. Hoşuma gitmiyor ama kullanmak. Spring framework geliştiricileri bu durum üzerinde çalışmalıdır!
digz6666

<Bean class = "EncodingPostProcessor" /> satırı nereye gidiyor?
zod

1
@zod: In DispatcherServlet's config ( ...-servlet.xml)
axtavt

Teşekkürler. Görmezden geliniyor. Mvc (sanırım) kullanıyoruz ve giriş noktası gibi görünen @Controller niteliğine sahip bir sınıfımız var. Sınıftan başka hiçbir yerde bahsedilmemiştir (benzer ada sahip bir arayüze sahiptir) ancak somutlaştırılmış ve doğru şekilde çağrılmıştır. Yollar bir @RequestMapping özniteliğiyle eşlenir. Yanıtın içerik türünü kontrol edemiyoruz (xml'ye ihtiyacımız var). Muhtemelen söyleyebileceğiniz gibi, ne yaptığım hakkında hiçbir fikrim yok ve bunu yaratan geliştirici şirketimi terk etti. Teşekkürler.
zod

3
@ Digz6666'nın söylediği gibi bunun kirli bir hack olduğunu. Bahar, JAX-RS'nin bunu nasıl yaptığını görmeli.
Adam Gent

166

Bahar 3.1 için çözüm buldum. @ResponseBody ek açıklamasını kullanarak. Json çıktısını kullanan denetleyici örneği:

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

}

7
+1. Bu benim için de çözdü, ancak yalnızca <mvc:annotation-driven/>applicationContext'te kullanmaya geçtikten sonra . (Bunun yerine, <bean class=" [...] DefaultAnnotationHandlerMapping"/>Bahar 3.2'de zaten kullanımdan kaldırıldı ...)
Jonik

Bu şekilde açıklama eklenmişse, bu uygulama / xml üretmek için?
Hurda

2
@Hurda: Açıkçası, producesözniteliğin değerini değiştirerek istediğiniz herhangi bir içerik türünü belirtebilirsiniz .
Jonik

1
"Application / json" için de bir MediaType.APPLICATION_JSON_VALUE vardır.
dev

2
UTF-8 için bkz MediaType.APPLICATION_JSON_UTF8_VALUE.
calvinf

51

Spring MVC 3.1'de ileti dönüştürücülerini yapılandırmak için MVC ad alanını kullanabileceğinizi unutmayın:

<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>

Veya kod tabanlı yapılandırma:

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

1) Accept-CharsetMuhtemelen bilinen her karakter kodlamasını listeleyen bir başlıkla yanıtı kirletir ve 2) istek bir Acceptbaşlığa supportedMediaTypessahip olduğunda dönüştürücünün özelliği kullanılmaz , bu nedenle örneğin istek yazmayı yaptığımda doğrudan bir tarayıcıdaki URL'nin Content-Type: text/htmlyerine yanıtın bir başlığı vardır .
Giulio Piancastelli

3
"Metin / düz" varsayılan olarak zaten basitleştirebilirsiniz: <bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean>
Igor Mukhin

Bu cevap doğru cevap olarak kabul edilmelidir. Ayrıca @IgorMukhin'in StringHttpMessageConverter bean tanımlama yöntemi çalışır. Bu yanıt, tüm sunucu uygulamaları için yanıt içerik türlerini ayarlamak için kullanılır. Yalnızca belirli bir denetleyici yöntemi için yanıt içerik türünü ayarlamanız gerekiyorsa, bunun yerine Warrior'un yanıtını kullanın (@RequestMapping'de üretir argüman kullanın)
PickBoy

3
@GiulioPiancastelli İlk sorunuz, çekirdeğe <property name = "writeAcceptCharset" value = "false" />
ekleyerek çözülebilir

44

Her ihtimale karşı şu şekilde de kodlamayı ayarlayabilirsiniz:

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

StringHttpMessageConverter kullanmanın bundan daha iyi olduğunu düşünüyorum.


the manifest may not be valid or the file could not be opened.IE 11'de hata alırsanız çözüm de budur. Teşekkürler digz!
Arun Christopher

21

RequestMapping'e üretir = "text / plain; charset = UTF-8" ekleyebilirsiniz

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

daha fazla ayrıntı için bu bloga bakın


2
Bu kod derlenmez; geçersiz bir yöntemden bir şeyi geri veriyorsunuz.
Andrew Swan

2
üzgünüm kötü hata, şimdi düzeltildi
Charlie Wu

3
Bu yanlış bir cevap. İlkbahar belgelerine göre: Eşlenen isteğin üretilebilir ortam türleri, birincil eşlemeyi daraltır. Biçim, bir medya türleri dizisidir ("metin / düz", "uygulama / *), istek yalnızca Kabul Et bu ortam türlerinden biriyle eşleşirse eşlenir. İfadeler, olduğu gibi"! "Operatörü kullanılarak reddedilebilir. "! metin / düz", "metin / düz" dışındaki bir Kabul ile eşleşen tüm isteklerle eşleşen "! metin / düz".
Oleksandr_DJ

@CharlieWu Bağlantıyla ilgili bir sorun var
Matt

10

Son zamanlarda bu sorunla mücadele ediyordum ve Bahar 3.1'de çok daha iyi bir cevap buldum:

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

Yani, JAX-RS kadar kolay, tıpkı tüm yorumların gösterdiği / olması gerektiği gibi.


Bahar 3.1'e taşınmaya değer!
young.fu.panda

5
@dbyoung Bu doğru görünmüyor, javadoc producesşöyle diyor: "... istek yalnızca Content-Type bu ortam türlerinden biriyle eşleşirse eşlenir." Bu, AFAIK'in produces, yöntemin bir taleple eşleşip eşleşmediğiyle ilgili olduğu ve yanıtın hangi içerik türüne sahip olması gerektiği ile ilgili olmadığı anlamına gelir .
Ittai

@Ittai doğru! "üretir", yöntemin istekle eşleşip eşleşmediğini belirler, ancak yanıtta hangi içerik türünün olduğunu DEĞİLDİR. Hangi içerik türünün ayarlanacağını belirlerken başka bir şeyin "üretiyor" a bakıyor olması gerekir
anton1980

6

Kontrolörden gönderdiğiniz yanıtın türünü belirtmek için üretimleri kullanabilirsiniz. Bu "üretir" anahtar sözcüğü ajax isteğinde çok yararlı olacak ve projemde çok yardımcı oldu

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

public @ResponseBody String getMobileData() {

}

4

Teşekkürler digz6666, çözümünüz küçük bir değişiklikle benim için çalışıyor çünkü json kullanıyorum:

responseHeaders.add ("İçerik Türü", "application / json; charset = utf-8");

Axtavt tarafından verilen cevap (tavsiye ettiğiniz) benim için işe yaramayacak. Doğru medya türünü eklesem bile:

if (conv instanceof StringHttpMessageConverter) {                   
                    ((StringHttpMessageConverter) dönüşüm) .setSupportedMediaTypes (
                        Arrays.asList (
                                yeni MediaType ("text", "html", Charset.forName ("UTF-8")),
                                new MediaType ("uygulama", "json", Charset.forName ("UTF-8"))));
                }

4

İçerik türünü ContentNegotiatingViewResolver beaninde MarshallingView'da ayarladım . Kolay, temiz ve sorunsuz çalışır:

<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>

3

Web.xml'de yapılandırılan CharacterEncodingFilter'ı kullanıyorum. Belki bu yardımcı olur.

    <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>

1
Bu sadece istekte bulunan karakteri filtreler, yanıt olarak değil - Bunu zaten kullanıyorum
Hurda

@Hurda: Bununla forceEncoding=trueyanıtı da filtreler, ancak bu durumda yardımcı olmaz.
axtavt

Şimdiye kadarki en iyi ve daha hızlı cevap. Ben de zaten bu filtreyi bildiriyor ve kullanıyordum ama ile forceEncoding=false. Sadece olarak ayarladım falseve "charset = UTF-8" Content-Typebaşlığa başarıyla eklendi .
Saad Benbouzid

2

Yukarıdakilerin hiçbiri sizin için işe yaramadıysa, "GET" yerine "POST" üzerinden ajax isteklerinde bulunmaya çalışırsanız, bu benim için iyi çalıştı ... Yukarıdakilerin hiçbiri işe yaramadı. Ayrıca encodingFilter karakterine sahibim.


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");
    }
}

Bu sorun için birçok geçici çözümü denedikten sonra .. Bunu düşündüm ve iyi çalışıyor.


2

Bahar 3.1.1'de bu sorunu çözmenin basit yolu şudur: aşağıdaki yapılandırma kodlarını 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>

Hiçbir şeyi geçersiz kılmanıza veya uygulamanıza gerek yok.


2

Bu sorunu aşağıdaki yapılandırmayla çözmeye karar verirseniz:

<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 dosyanızın tamamında yalnızca bir mvc: ek açıklamaya dayalı etiket olması gerektiğini onaylamanız gerekir. aksi takdirde konfigürasyon etkili olmayabilir.


1

Göre bağlantı UTF-8'e = yayı 3.1 kullanarak veya daha sonra ayarlanan karakter kümesine nadas yapılandırmayı kullanıyorsa .Eğer "bir karakter kodlama belirtilmemişse, Servlet şartname ISO-8859-1 bir kodlama kullanılır gerektirir" yanıt gövdesi
@RequestMapping (değer = "eşleme url'niz", üretir = "metin / düz; karakter kümesi = UTF-8")


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

Örnek yapılandırma:

    <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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.