MockMvc nie obsługuje już znaków UTF-8 w Spring Boot 2.2.0

14

Po aktualizacji do nowo wydanej 2.2.0.RELEASEwersji Spring Boot niektóre z moich testów zakończyły się niepowodzeniem. Wygląda na to, że MediaType.APPLICATION_JSON_UTF8został on uznany za przestarzały i nie jest już zwracany jako domyślny typ zawartości z metod kontrolera, które nie określają jawnie typu zawartości.

Kod testowy jak

String content = mockMvc.perform(get("/some-api")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andReturn()
            .getResponse()
            .getContentAsString();

nagle przestał działać, ponieważ typ zawartości był niezgodny, jak pokazano poniżej

java.lang.AssertionError: Content type 
Expected :application/json;charset=UTF-8
Actual   :application/json

Na razie zmieniam kod, aby .andExpect(content().contentType(MediaType.APPLICATION_JSON))rozwiązać problem.

Ale teraz w porównaniu contentz oczekiwanym serializowanym obiektem nadal występuje niedopasowanie, jeśli w obiekcie znajdują się jakieś znaki specjalne. Wygląda na to, że .getContentAsString()metoda nie wykorzystuje domyślnie kodowania znaków UTF-8 (więcej).

java.lang.AssertionError: Response content expected:<[{"description":"Er hörte leise Schritte hinter sich."}]> but was:<[{"description":"Er hörte leise Schritte hinter sich."}]>
Expected :[{"description":"Er hörte leise Schritte hinter sich."}]
Actual   :[{"description":"Er hörte leise Schritte hinter sich."}]

Jak mogę uzyskać contentkodowanie UTF-8?

Czasy
źródło

Odpowiedzi:

7

Tak. Jest to problem z wersji wiosennej 2.2.0. Ustawiają wycofanie dla domyślnego kodowania zestawu znaków.

.getContentAsString(StandardCharsets.UTF_8) - dobrze, ale w każdej odpowiedzi domyślnie zostanie wypełniona ISO 8859-1.

W moim projekcie zaktualizowałem aktualnie utworzony konwerter:

@Configuration
public class SpringConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
            .findFirst()
            .ifPresent(converter -> ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(UTF_8));
    }
...
Alexander Someoneperson
źródło
To było najłatwiejsze rozwiązanie zalecane tutaj!
Times
uratowałeś mi dzień!
Filomat
2

Domyślny znak kodowania nie jest już UTF-8 od wersji wiosennej 5.2.0.

Aby kontynuować korzystanie z UTF-8, musisz ustawić go w odpowiedzi ServletRock wyniku MockMvc. Aby ustawić domyślne kodowanie znaków na UTF-8, wykonaj coś takiego w metodzie konfiguracji:

@Before
public void setUp() {
   mockMvc = webAppContextSetup(wac).addFilter(((request, response, chain) -> {
                response.setCharacterEncoding("UTF-8");
                chain.doFilter(request, response);
            })).build();
}

Następnie możesz użyć instancji mockMvc, aby wykonać żądanie.

Mam nadzieję, że to pomoże.

black4bird
źródło
Dzięki temu rozwiązaniu musiałbym skonfigurować mockMvc w każdej klasie testowej. Może to być dość nudne dla wielu klas testowych!
Times
0

Zgodnie z tym żądaniem ściągania od deweloperów wiosennych nagłówek UTF-8 nie jest już wymagany i dlatego jest przestarzały. Jeśli używasz nagłówka UTF-8 w swojej aplikacji, możesz spróbować usunąć go z aplikacji zamiast próbować naprawić test. Upewnij się tylko, że używasz nagłówka Content-Type: application / json i wszystko powinno być w porządku.

scre_www
źródło
Myślę, że nie rozumiesz problemu. Proponuję przeczytać całe pytanie, a następnie ponownie je ocenić, jeśli twoja odpowiedź ma jakąkolwiek wartość. Moja aplikacja jest w porządku, problem jest związany z testami.
Times
Przeczytałem ponownie całe pytanie i ponownie oceniłem swoją odpowiedź, odpowiedź jest nadal taka sama. W swoim pytaniu nie wyjaśniasz, dlaczego nagłówek jest przestarzały, wzbogaciłem twoje pytanie o mój post. Sugeruję przeczytanie PR, z którym się połączyłem, aby zrozumieć, dlaczego nagłówek jest nieaktualny. Jeśli rozumiesz dlaczego, możesz rozważyć zmianę testu, ponieważ testuje on domyślne zachowanie w Spring 2.1.X, ale nie testuje to poprawnie w Spring 2.2.X. Zmieniło się zachowanie wiosny, test powinien się odpowiednio zmienić, jeśli zaakceptujesz nowe zachowanie wiosny.
scre_www
Nie jesteś tutaj bardzo konsekwentny. W swojej odpowiedzi mówisz „[...] zamiast próbować naprawić swój test”. W swoim komentarzu mówisz „[...] twój test powinien się odpowiednio zmienić, jeśli zaakceptujesz nowe wiosenne zachowanie”.
Times
Każdy programista od czasu do czasu napotyka przestarzałe wartości. Kiedy coś jest przestarzałe, możesz to jakoś naprawić, nie badając, dlaczego stało się przestarzałe. To podejście wydaje się być sposobem na rozwiązanie tego problemu. Teraz sugeruję, abyś spojrzał dalej i zbadał, dlaczego stał się przestarzały. Jeśli to zrozumiesz, możesz podjąć lepszą decyzję, co dalej. W twoim pytaniu nie ma nic o tym, dlaczego mówisz nam tylko, że test kończy się niepowodzeniem z powodu przestarzałej wartości, która jest złym badaniem. Wzbogaciłem to pytanie o niektóre badania, których nie przeprowadziłeś ORAZ głosowałem za Q.
scre_www
0

Używam Spring Boot 1.5.15. RELEASE i napotkałem ten sam problem podczas pisania testów.

Pierwszym rozwiązaniem, które pomogło mi, było dodanie .characterEncoding („UTF-8”)) w następujący sposób:

String content = mockMvc.perform(get("/some-api")
            .contentType(MediaType.APPLICATION_JSON)
            .characterEncoding("UTF-8"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andReturn()
            .getResponse()
            .getContentAsString();

Używam StandaloneMockMvcBuilder w mojej klasie testowej, więc drugim rozwiązaniem, które pomogło mi, było utworzenie filtra, np .:

private static class Utf8Filter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        filterChain.doFilter(request, response);
    }
}

a później dodaj go do metody standaloneSetup w mojej klasie testowej w następujący sposób:

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    final SomeResource someResource = new SomeResource(someService);
    this.restLankMockMvc = MockMvcBuilders.standaloneSetup(someResource)
        .setCustomArgumentResolvers(pageableArgumentResolver)
        .setControllerAdvice(exceptionTranslator)
        .setConversionService(createFormattingConversionService())
        .setMessageConverters(jacksonMessageConverter)
        .addFilter(new Utf8Filter())
        .build();
}
Zuljen
źródło
0

Dodatkowe ustawienie MockMvc .accept(MediaType.APPLICATION_JSON_UTF8_VALUE):

    String content = mockMvc.perform(get("/some-api")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andReturn()
        .getResponse()
        .getContentAsString();

Ten problem nie dotyczy Spring Boot, ale chyba konkretny MockMvc. Tak więc obejście musi być zastosowane tylko do MockMvc. ( JSON musi być zakodowany przy użyciu UTF-8 .)

powiązany problem: niewłaściwa obsługa UTF-8 w MockMvc dla odpowiedzi JSON · Problem # 23622 · wiosenne projekty / wiosenne ramy

yukihane
źródło