Spring MVC(@ResponseBody)で応答コンテンツタイプを設定する人


126

私は注釈付きのSpring MVC Java Webアプリケーションを突堤Webサーバー(現在はMaven Jetty Plugin)で実行しています。

文字列ヘルプテキストだけを返す1つのコントローラーメソッドでAJAXサポートを実行しようとしています。リソースはUTF-8エンコーディングであり、文字列もそうですが、サーバーからの応答には

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

ブラウザが送信したときでも

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

どういうわけか春のデフォルト構成を使用しています

このBeanを構成に追加するためのヒントを見つけましたが、エンコーディングをサポートしておらず、代わりにデフォルトのBeanが使用されているため、使用されていないと思います。

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

回答:


59

StringHttpMessageConverterBeanの単純な宣言だけでは十分ではありません。これをに注入する必要があります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のを再定義する必要があります<mvc:annotation-driven />。また、では機能しません。

したがって、おそらく最も便利ですが醜い方法はAnnotationMethodHandlerAdapterwithのインスタンス化をインターセプトすることです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
それは汚いハックのようです。嫌いなのに使う。Springフレームワーク開発者はこのケースに取り組むべきです!
digz6666 2011年

<bean class = "EncodingPostProcessor" />という行はどこに行くのですか?
zod '30年

1
@zod:in DispatcherServletの構成(...-servlet.xml
axtavt

ありがとう。無視されているようです。私たちはmvcを使用しています(私はそう思います)、@ Controller属性を持つクラスがあります。これはエントリポイントのようです。クラスはどこにも言及されていません(同様の名前のインターフェースがあります)が、インスタンス化されて正しく呼び出されます。パスは@RequestMapping属性でマップされます。応答のコンテンツタイプを制御できません(xmlが必要です)。おそらくお分かりのように、私は何をしているのかわかりません。これを作成した開発者が退職しました。ありがとう。
zod 2011年

3
@ digz6666が言うように、これは汚いハックです。Springは、JAX-RSがどのように実行するかを確認する必要があります。
アダム・ゲント

166

Spring 3.1の解決策を見つけました。@ResponseBodyアノテーションを使用します。以下は、Json出力を使用するコントローラーの例です。

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

}

7
+1。これも私にとってそれを解決しましたが、<mvc:annotation-driven/>applicationContextでの使用に切り替えた後のみです。(<bean class=" [...] DefaultAnnotationHandlerMapping"/>とにかく、Spring 3.2で非推奨となる...の代わりに)
Jonik 2013

caこの方法で注釈を付けると、application / xmlが生成されますか?
フルダ

2
@Hurda:もちろん、produces属性の値を変更することで、必要な任意のコンテンツタイプを指定できます。
Jonik 2013年

1
「application / json」用のMediaType.APPLICATION_JSON_VALUEもあります。
開発者2014年

2
UTF-8については、を参照してくださいMediaType.APPLICATION_JSON_UTF8_VALUE
calvinf 2016

51

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

1)それAccept-Charsetはおそらくすべての既知の文字エンコーディングをリストするヘッダーで応答を汚染し、2)リクエストにAcceptヘッダーがある場合supportedMediaTypes、コンバーターのプロパティは使用されないので、たとえばリクエストを入力するとContent-Type: text/html代わりにブラウザのURLに直接応答が含まれます。
Giulio Piancastelli、2015

3
:「text / plainでは」とにかくデフォルトであるとして、あなたは簡素化することができます <bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean>
イゴールMukhin

この答えは正しい答えとして受け入れられるべきです。また、@IgorMukhinのStringHttpMessageConverter Beanを定義する方法も機能します。この回答は、すべてのサーブレットの応答コンテンツタイプを設定するために使用されます。特定のコントローラーメソッドに応答コンテンツタイプを設定する必要があるだけの場合は、代わりにWarriorの回答を使用します(@RequestMappingで引数を生成します)
PickBoy

3
@GiulioPiancastelli最初の質問は、Beanに<property name = "writeAcceptCharset" value = "false" />を追加することで解決できます
PickBoy

44

念のため、次の方法でエンコーディングを設定することもできます。

@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を使用する方がこれより優れていると思います。


これはthe manifest may not be valid or the file could not be opened.、IE 11でエラーが発生した場合の解決策でもあります。ありがとうdigz!
アルンクリストファー

21

あなたはRequestMappingにproduces = "text / plain; charset = UTF-8"を追加できます

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

詳細については、このブログを参照してください


2
そのコードはコンパイルされません。あなたはvoidメソッドから何かを返しています。
アンドリュース

2
申し訳ありませんが、バグは修正されました
チャーリーウー

3
不正解です。春のドキュメントによると:プライマリマッピングを絞り込む、マッピングされたリクエストの生成可能なメディアタイプ。フォーマットは一連のメディアタイプ(「text / plain」、「application / *」)であり、Acceptがこれらのメディアタイプのいずれかに一致する場合にのみリクエストがマッピングされます。式は、「!」演算子を使用して否定できます。 「!text / plain」は、「text / plain」以外のAcceptを含むすべてのリクエストに一致します
Oleksandr_DJ

@CharlieWuリンクに問題があります
Matt

10

私は最近この問題と戦っていましたが、Spring 3.1で利用できるはるかに良い答えを見つけました:

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

したがって、すべてのコメントがそうすることができる/すべきであると示したように、JAX-RSと同じくらい簡単です。


Spring 3.1に移植する価値がある!
young.fu.panda

5
@dbyoungこれは正しくないようですproduces。「...リクエストは、Content-Typeがこれらのメディアタイプのいずれかに一致する場合にのみマップされます。」そのことを私の知る限りを意味producesする方法が要求していないどのようにどのコンテンツタイプ応答が持つべきと一致するかどうかに関連しています。
イタイ

@イタイ正解!"produces"は、メソッドがリクエストに一致するかどうかを決定しますが、応答に含まれるコンテンツタイプは決定しません。設定するコンテンツタイプを決定するときに、他の何かが「プロデュース」を見ている必要があります
anton1980

6

生成物を使用して、コントローラーから送信する応答のタイプを示すことができます。この "produces"キーワードは、ajaxリクエストで最も役立ち、私のプロジェクトで非常に役立ちました。

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

public @ResponseBody String getMobileData() {

}

4

digz6666に感謝します。jsonを使用しているため、ソリューションはわずかな変更で機能します。

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

axtavtからの回答(どの方法を推奨したか)はうまくいきません。正しいメディアタイプを追加した場合でも、

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

4

ContentNegotiatingViewResolver BeanのMarshallingViewでコンテンツタイプを設定しました。簡単、クリーン、スムーズに機能します。

<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で構成されたCharacterEncodingFilterを使用しています。多分それは役立ちます。

    <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
これは単に、要求ではなく、応答に文字をフィルタリング-私は出回っこの1を使用しています
Hurda

@Hurda:ではforceEncoding=true、それはあまりにもレスポンスをフィルタリングし、それはこの場合には役に立ちません。
axtavt

これまでのところ、最善かつ迅速な回答です。また、私はすでになく、と、宣言してこのフィルタを使用していましたforceEncoding=false。これをに設定するとfalse、「charset = UTF-8」がContent-Typeヘッダーに正常に追加されます。
Saad Benbouzid、2016年

2

上記のいずれも機能せず、「GET」ではなく「POST」でajaxリクエストを実行しようとした場合、それはうまく機能しました...上記のいずれも機能しませんでした。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");
    }
}

この問題の多くの回避策を試した後、私はこれを考え、それがうまくいきました。


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>

何もオーバーライドまたは実装する必要はありません。


2

次の構成でこの問題を修正する場合:

<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-drivenタグが1つだけあることを確認する必要があります。そうしないと、構成が有効にならない可能性があります。


1

リンクによれば、「文字エンコーディングが指定されていない場合、サーブレット仕様ではISO-8859-1のエンコーディングを使用する必要があります」。応答本文
@RequestMapping(value = "your mapping url"、produces = "text / plain; charset = 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;
        }
    }
}

構成例:

    <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>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.