Spring MVC에서 응답 컨텐츠 유형을 설정하는 사람 (@ResponseBody)


126

Annotation 기반의 Spring MVC Java 웹 응용 프로그램을 부두 웹 서버 (현재 maven jetty plugin)에서 실행하고 있습니다.

String 도움말 텍스트 만 반환하는 하나의 컨트롤러 메소드로 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
더러운 해킹처럼 보입니다. 나는 그것을 좋아하지만 사용하는 것이 아닙니다. 스프링 프레임 워크 개발자는이 경우에 노력해야합니다!
digz6666

<bean class = "EncodingPostProcessor"/> 줄은 어디로 갑니까?
zod

1
@zod : DispatcherServlet의 설정 ( ...-servlet.xml)
axtavt

감사. 무시 된 것 같습니다. 우리는 mvc (제 생각에)를 사용하고 있으며 @Controller 속성을 가진 클래스를 가지고 있습니다. 클래스는 다른 곳에서는 언급되지 않지만 (유사한 이름의 인터페이스가 있음) 인스턴스화되고 올바르게 호출됩니다. 경로는 @RequestMapping 속성으로 매핑됩니다. 응답의 컨텐츠 유형을 제어 할 수 없습니다 (xml이 필요함). 당신이 아마 알 수 있듯이, 나는 내가하고있는 일을 모른다. 그리고 이것을 만든 개발자가 내 회사를 떠났다. 감사.
zod

3
@ digz6666이 말했듯이 이것은 더러운 핵입니다. 봄은 JAX-RS가 어떻게해야하는지 알아야합니다.
Adam Gent

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

ca 이런 식으로 주석이 달린 경우 application / xml을 생성합니까?
Hurda

2
@Hurda : produces속성 값을 변경하여 원하는 컨텐츠 유형을 지정할 수 있습니다 .
Jonik

1
"application / json"에 대한 MediaType.APPLICATION_JSON_VALUE도 있습니다.
dev

2
UTF-8의 경우을 참조하십시오 MediaType.APPLICATION_JSON_UTF8_VALUE.
calvinf

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경우 변환기 의 속성이 사용되지 않는 경우 를 제외하고는 예를 들어 요청을 입력 할 때를 제외하고는 일종의 작업 브라우저의 URL에 직접 응답에 Content-Type: text/html헤더가 있습니다.
Giulio Piancastelli 2019

3
당신은 단순화 할 수 있습니다 "텍스트 / 일반"기본 어쨌든입니다 : <bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean>
이고르 Mukhin

이 답변은 정답으로 받아 들여 져야합니다. 또한 @IgorMukhin의 StringHttpMessageConverter Bean을 정의하는 방법이 작동합니다. 이 답변은 모든 서블릿에 대한 응답 컨텐츠 유형을 설정하는 데 사용됩니다. 특정 컨트롤러 메소드에 대한 응답 컨텐츠 유형을 설정 해야하는 경우 대신 Warrior의 답변을 사용하십시오 (@RequestMapping에서 인수를 생성하십시오)
PickBoy

3
@GiulioPiancastelli 첫 번째 질문은 <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!
Arun Christopher

21

RequestMapping에 produce = "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
미안 나쁜 버그, 이제 수정되었습니다
Charlie Wu

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 옳지 않은 것처럼,에 대한 javadoc은 produces"... 콘텐츠 유형이 이러한 미디어 유형 중 하나와 일치하는 경우에만 요청됩니다." 즉, AFAIK produces는 메소드가 요청과 일치하는지 여부와 응답이 어떤 컨텐츠 유형을 가져야하는지 여부와 관련이 없음 을 의미 합니다.
Ittai

@Ittai 맞습니다! "produces"는 메소드가 요청과 일치하는지 여부를 판별하지만 응답에있는 컨텐츠 유형은 아닙니다. 설정할 컨텐츠 유형을 결정할 때 다른 무언가가 "
제작물"을보아야

6

농작물을 사용하여 컨트롤러에서 보내는 응답의 유형을 나타낼 수 있습니다. 이 "produces"키워드는 아약스 요청에서 가장 유용하며 내 프로젝트에서 매우 유용했습니다.

@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) 전환) .setSupportedMediaTypes (
                        Arrays.asList (
                                새로운 MediaType ( "text", "html", Charset.forName ( "UTF-8")),
                                새로운 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
이것은 단지하지 응답, 요청 문자를 필터링 - 나는 allready이 하나를 사용하고 있습니다
Hurda

@Hurda : forceEncoding=true응답을 필터링하지만이 경우 도움이되지 않습니다.
axtavt

지금까지 가장 빠른 답변. 또한 이미이 필터를 선언하고 사용하고 있었지만로했습니다 forceEncoding=false. 방금 설정 false하고 "charset = UTF-8"이 Content-Type헤더에 추가되었습니다 .
Saad Benbouzid

2

위의 어느 것도 효과가 없다면 "GET"이 아닌 "POST"에 대한 아약스 요청을 시도하면 나에게 잘 작동했습니다 ... 위의 어느 것도하지 않았습니다. 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

에 따르면, 링크 는 UTF-8로 = 스프링 3.1 이상을 사용하는 일련의 캐릭터 세트에 fallowing 구성을 사용하는 .If "문자 인코딩을 지정하지 않으면, 서블릿 규격은 ISO-8859-1의 인코딩이 사용되어야합니다" 응답 본문
@RequestMapping (value = "매핑 URL", 생성 = "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.