Spring Boot执行器端点的响应MIME类型

28

我已经将一个Spring Boot应用程序从1.4.x更新到1.5.1,现在Spring Actuator端点返回不同的MIME类型:

例如,/health 现在是 application/vnd.spring-boot.actuator.v1+json 而不是简单的 application/json 了。

我该如何将其更改回来?

6个回答

22

端点返回符合客户端请求所能接受的内容类型。如果客户端发送了一个要求使用application/jsonAccept头,则会收到相应的JSON响应:

Accept: application/json

3
好的,至少这适用于我的应用程序和其他表现良好的应用程序。对于 Firefox,我需要查找一些设置,它会始终下载端点而不是显示它们。谢谢。 - kap
4
我也遇到了同样的问题,不知道为什么要改变。在我们公司中,更改所有浏览器的设置有点麻烦。我甚至还没找到如何在Safari或智能手机浏览器上更改这些设置。这个更改是否与以下问题有关?https://github.com/spring-projects/spring-boot/issues/7648 - Thomas Oellrich
“Break things” 意味着更改响应主体的结构。一个写得好的客户端不应受到在 1.5 中所做的更改的影响。 - Andy Wilkinson
1
谢谢,但我不认为我会将那些信息转发给移动应用程序开发人员 :-) 相反,我会像下面这样重写健康API以保持事情的顺畅。 - hirro
3
在使用Firefox浏览器时需要注意,新版本的浏览器内置了一个很好用的JSON查看器,但是却无法识别application/vnd.spring-boot.actuator.v1+json这种JSON类型。你可以使用JSONView插件,并勾选两个选项:在HTTP请求头中包含"application/json" 和使用Firefox内置的JSON查看器。这样浏览器就会请求application/json类型的响应,同时JSONView不会干扰响应的读取,Firefox会将其作为普通的JSON读取。 - jihor
显示剩余2条评论

14

回应https://stackoverflow.com/users/2952093/kap的评论(我的声望值不够高,无法发表评论):当使用Firefox检查返回JSON的端点时,我使用Add-on JSONView。在设置中有一个选项可以指定替代的JSON内容类型,只需添加application/vnd.spring-boot.actuator.v1+json,您就可以在浏览器内以漂亮的格式打印查看返回的JSON。


5

自从SpringBoot 2.0.x版本以后,实现EndpointHandlerMappingCustomizer的建议解决方案已经不再适用。

好消息是,现在的解决方案更简单了。

需要提供Bean EndpointMediaTypes。默认情况下,它由SpringBoot类WebEndpointAutoConfiguration提供。

提供您自己的可以像这样:

@Configuration
public class ActuatorEndpointConfig {

    private static final List<String> MEDIA_TYPES = Arrays
        .asList("application/json", ActuatorMediaType.V2_JSON);

    @Bean
    public EndpointMediaTypes endpointMediaTypes() {
        return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES);
    }
}

5

你可能已经注意到,1.5.x版本中执行器的内容类型已发生变化。

如果你在"Accept:"头中输入"application/json",你应该会得到通常的内容类型。

但如果你没有任何修改客户端的方法,这段代码将返回健康信息(不包括细节)和原始内容类型(即1.4.x版本的方式)。

@RestController
@RequestMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE)
public class HealthController {

    @Inject
    HealthEndpoint healthEndpoint;
    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity<Health > health() throws IOException {
        Health health = healthEndpoint.health();
        Health nonSensitiveHealthResult = Health.status(health.getStatus()).build();
        if (health.getStatus().equals(Status.UP)) {
            return ResponseEntity.status(HttpStatus.OK).body(nonSensitiveHealthResult);
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(nonSensitiveHealthResult);
        }
    }
}

配置(移除现有健康状况)

endpoints.health.path: internal/health

4

基于https://github.com/spring-projects/spring-boot/issues/2449中的代码(该代码完全移除了新类型,但仍然有效),我得出以下结论:

@Component
public class ActuatorCustomizer implements EndpointHandlerMappingCustomizer {

    static class Fix extends HandlerInterceptorAdapter {


        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            Object attribute = request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
            if (attribute instanceof LinkedHashSet) {
                @SuppressWarnings("unchecked")
                LinkedHashSet<MediaType> lhs = (LinkedHashSet<MediaType>) attribute;
                if (lhs.remove(ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON)) {
                    lhs.add(ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON);
                }
            }
            return true;
        }

    }

    @Override
    public void customize(EndpointHandlerMapping mapping) {
        mapping.setInterceptors(new Object[] {new Fix()});
    }
}

它将新的供应商媒体类型放在最后,这样当没有指定内容时,它将在所有执行器端点中使用 application/json

已测试与spring-boot 1.5.3。


快速问题:if (...was removed...) { ...re-add...}HashSet.remove() 仅在元素存在时才删除。这个方法是有效的,但为什么呢? :-) - crusy
你的代码将 [application/vnd.spring-boot.actuator.v1+json, application/json] 切换为 [application/json, application/vnd.spring-boot.actuator.v1+json]。以下方法同样有效(并且可能更少令人困惑):lhs.remove(ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON); lhs.add(MediaType.APPLICATION_JSON); - crusy
1
@crusy 我不知道那个特殊的新类型是用于什么目的,所以我不想将其删除。它可以留在支持的媒体类型列表中,这样需要它的代码仍然可以请求它,但对于我来说,它应该在 application/json 之后,因为我想使用默认浏览器获取 json。由于 LinkedHashSet 保持插入顺序,我只需将其删除并重新添加以将其排序到后面。使用 if 来确保我不会修改一开始没有这种新类型的东西。 - zapl
在Spring Boot 1.5.4.RELEASE和Grails 3.3.0.RC1中对我有效,但前提是我将自定义方法更改为包含mapping.setInterceptors(new Fix()) - jerryb

2

它说对于Firefox 58+将不再需要:“此插件的目的已经被纳入Firefox 58的本地集成中。” - AndreLDM
1
我们如何在Firefox 58+中打开覆盖?我使用的是62,但Firefox仍然要求保存文件而不是直接显示它。 - dan carter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接