Как настроить весеннюю загрузку 2 для возврата xml по умолчанию?

Для начала я прочитал это:

Spring boot - установка содержимого по умолчанию заголовок -type, если его нет в запросе

Старая версия этого работала при весенней загрузке 1. Однако при получении запроса со следующим заголовком принятия Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"

Ответ в json.

я поставил в классе

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(
            ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_XML);
    }

}

И я вижу, что устанавливается стратегия defaultContentType. Однако он перезаписывается стратегией AcceptHeaderConfig.

Похоже, что defaultContentType используется только как запасной вариант.

Обратите внимание, что тот же код в весенней загрузке 1 работал и по умолчанию использовал XML.


Полный пример

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.annotation.XmlRootElement;

@SpringBootApplication
@RestController
public class CnApp {

    @RequestMapping("/")
    public Person person(HttpServletRequest request, ModelMap model){
        return new Person();
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(CnApp.class, args);
    }

    @XmlRootElement
    public static class Person {
        public String firstName = "Jon";
        public String lastName = "Doe";
    }

    @Configuration
    public static class ServerConfig implements WebMvcConfigurer {
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.defaultContentType(MediaType.APPLICATION_XML);
        }

    }

}

как вы можете видеть, запустив curl localhost:8080 -H"Accept: text/html, image/gif, image/jpg;q=0.2, */*;q=0.2" По умолчанию используется json, хотя по умолчанию используется XML


Из комментария, который я разместил ниже

Проблема в старой версии Spring, которую мы можем отправить с заголовком accept и получить запросы, для которых по умолчанию используется XML. Однако JSON по-прежнему поддерживается.

Поэтому, когда приходит заголовок accept, который поддерживает как JSON, так и XML с одинаковой специфичностью, нам нужно вернуть XML.


person Wes    schedule 12.01.2021    source источник


Ответы (2)


Ваша конфигурация WebMvc работает так же хорошо, как вы ее настроили.

По умолчанию используется ContentType, если отсутствует заголовок Accept.

Чтобы достичь своей области, вы должны продолжить стратегию согласования контента и отключить заголовок Accept. Ваш метод configureContentNegotiation должен выглядеть так:

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(false)
    .parameterName("mediaType")
    .ignoreAcceptHeader(true)
    .useJaf(false)
    .defaultContentType(MediaType.APPLICATION_XML)
    .mediaType("xml", MediaType.APPLICATION_XML;
}

Вы можете посмотреть это статью в блоге Spring и эту статью о Baeldung.

person thepaoloboi    schedule 13.01.2021
comment
Хорошо, спасибо, но проблема в старой версии Spring, которую мы можем отправить с заголовком accept и получить запросы, для которых по умолчанию используется xml. Однако json по-прежнему поддерживается. Поэтому, когда приходит заголовок accept, который поддерживает как JSON, так и XML с одинаковой специфичностью, нам нужно вернуть XML. - person Wes; 13.01.2021

Изучаем это дальше. То, что @thepaoloboi предложил в своем ответе, верно: defaultMessageConverter действительно происходит, только если не происходит никакой другой формы согласования контента.

Чтобы исправить это, я просмотрел код, который использует org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor, и я вижу, что он зависит от порядка настроенных конвертеров.

Итак, в качестве взлома следующее работает как весной 1, так и весной 2.

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;

@SpringBootApplication
@RestController
public class CnApp {

    @RequestMapping("/")
    public Person person(HttpServletRequest request, ModelMap model){
        return new Person();
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(CnApp.class, args);
    }

    @XmlRootElement
    public static class Person {
        public String firstName = "Jon";
        public String lastName = "Doe";
    }

    @Configuration
    public static class ServerConfig extends WebMvcConfigurerAdapter {
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.defaultContentType(MediaType.APPLICATION_XML);
        }

        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

            converters.add(0, new Jaxb2CollectionHttpMessageConverter<>());
            converters.add(0, new Jaxb2RootElementHttpMessageConverter());
            System.out.println("Converters:" + converters);
        }
    }

}

Как это работает, так это установка преобразователям Jaxb2 более высокого приоритета, чем преобразователям jackson.

Это можно проверить следующим образом

curl localhost:8080 -H"Accept: text/html, image/gif, image/jpg;q=0.2, */*;q=0.2"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><firstName>Jon</firstName><lastName>Doe</lastName></person>%

curl localhost:8080 -H"Accept: text/html, image/gif, image/jpg;q=0.2, application/json, */*;q=0.2"
{"firstName":"Jon","lastName":"Doe"}%            

Обратите внимание, что если application/json указано где-либо в заголовке, это по-прежнему предпочтительнее.

Это все еще похоже на хак, и было бы хорошо, если бы был способ сортировать предпочтительные типы mime, не прибегая к добавлению или изменению порядка конвертеров.

person Wes    schedule 13.01.2021