【问题标题】:Default MediaType for Http RequestsHttp 请求的默认 MediaType
【发布时间】:2017-12-21 20:37:43
【问题描述】:

我的 SpringBoot 应用程序向外部 API 发出 HTTP 请求,所有这些都使用/生成 JSON。默认情况下,我的应用使用 Jackson 进行数据绑定,所有 HTTP 请求(使用 RestTemplate)显然都使用了 application/jsonAcceptContent-Type 标头。

最近我需要使用 Jackson xml 数据绑定库(不适用于 http 数据绑定),因此我在应用程序中包含了该依赖项,似乎 SpringBoot 已决定对所有传出 HTTP 隐式使用 application/xml请求。

如何将应用程序配置为在发出 HTTP 请求时默认使用 JSON,而无需在每个请求的标头中显式设置?

【问题讨论】:

  • 您找到解决方案了吗?我也遇到了同样的问题。
  • @RicardoPieper 你可以试试interceptor based solution?你试过了吗?
  • @RicardoPieper 也有点私人化,但我找到了this question。相同的问题,最近发布的,以及听起来像巴西人的姓氏 =) 那个人是你的同事吗?
  • @buræquete 这行不通。我们有许多 RestTemplate 的实例分布在运行时加载的许多存储库中(我们进行热重载),每个存储库都有一些初始化代码,这基本上涉及在某些时候编写 new RestTemplate()。我只是希望它继续选择使用 JSON 而不是 XML :( 不,我不认识那个人,但我会看看他的问题,看看有没有什么问题。他几天前发布了,我只是10 分钟前发现我有这个问题。
  • @RicardoPieper 您可以拥有一些基于 XML 的 RestTemplate 和一些基于 JSON 的 RestTemplate,而基于 JSON 的初始化逻辑将始终包含拦截器添加逻辑。那不行吗? +大声笑那真是巧合,我确定你们在同一家公司!我以为他是平民开发者,几周后问题升级到更高级的开发者

标签: spring spring-boot


【解决方案1】:

答案主要集中在@RicardoPieper 的案例上,但单个RestTemplate 案例(来自OP)仍然可以使用this intercept based solution

在我看来,最好是明确的,而不是依赖于任何RestTemplate 中的Content-Type 设置。因此,为所有调用设置标题值,例如;

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Post> entity = new HttpEntity<>(post, headers);
ResponseEntity<Post> response = someTemplate.postForEntity(uri, entity, Post.class);

但是对于你的情况,我想出了一个想法,再次做拦截解决方案,但从更高的角度来看(尽管它仍然有一个假设)

有一个上下文刷新事件的监听器;

@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private Map<String, RestTemplate> templates;

    public void onApplicationEvent(ContextRefreshedEvent event) {
        templates.entrySet().stream()
                .filter(this::isJsonBased)
                .map(Map.Entry::getValue)
                .forEach(template -> template.setInterceptors(Collections.singletonList(new JsonMimeInterceptor())));
    }

    private boolean isJsonBased(Map.Entry<String, RestTemplate> entry) {
        return entry.getKey().toLowerCase().contains("json");
    }
}

在这里,我在上下文中获取所有 RestTemplate bean(包括它们的 bean 名称,使用 Map autowire feature),并首先进行过滤,尽管这是假设部分,但我认为 "json"JSON 聚焦 RestTemplate bean 的名称,例如;

@Bean
public RestTemplate jsonTemplate() {
    return new RestTemplate();
}

并将拦截逻辑应用于所有这些 bean。

public class JsonMimeInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpHeaders headers = request.getHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return execution.execute(request, body);
    }
}

你怎么看? =) 它适用于我的演示应用程序,尽管需要某种方法来区分基于 XML 和基于 JSONRestTemplate bean。如果你可以创建这样的区分,你可以在isJsonBased()方法中添加那个逻辑,仍然使用这个想法!

【讨论】:

  • 我给你赏金的原因如下: 1 - 你投入了你的时间; 2 - 表明我的问题没有立即解决方案; 3 - 完美回答了 OP 的问题; 4 - 展示了对 spring 事件、拦截器和 DI 的有效使用,大量信息浓缩在一篇文章中,我一定会用作参考。我忘了提到那些模块不知道 Spring 的存在,甚至没有被 Spring DI 实例化......但在某种程度上这是一个谎言,因为它们无论如何都使用 RestTemplate。整个事情揭示了一个我们并不完全意识到的耦合。
【解决方案2】:

只需覆盖和配置WebMvcConfigurerAdapter#configureContentNegotiation(ContentNegotiationConfigurer)中的默认内容类型,如下所示:

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON);
    }
}

【讨论】:

  • 已经试过了,还是不行。传出请求似乎仍然使用 Content-Type application/xml 发送。外部 API 以 415 代码响应。当我查看RestTemplate 对象中的http 消息转换器时,它们是来自fasterxml 的xml,而不是json 的。我认为我也需要以某种方式配置这些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
  • 1970-01-01
  • 2022-11-25
  • 2016-09-29
  • 2019-07-18
  • 1970-01-01
相关资源
最近更新 更多