如何根据条件应用Spring消息转换器?

2024-03-15

我有一个控制器,其响应是驼峰式 json 值。现在我们正在用新版本重写代码,所需的响应位于snake_case中。

我添加了一个消息转换器并修改了对象映射器来设置setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

public class ResponseJSONConverter extends MappingJackson2HttpMessageConverter {

@Autowired
public ResponseJSONConverter(ObjectMapper objectMapper) {
    setObjectMapper(objectMapper);
  }
}

我已经向 spring 注册了这个转换器,并且它按预期工作。现在,我希望我的旧端点以驼峰命名法返回,以便向后兼容我的消费者,并以蛇形命名法返回新端点。

我尝试再使用一个带有简单对象映射器的消息转换器,而无需将camelCase设置为Snake case属性并在spring中注册。根据 spring 配置中声明的顺序,仅应用一个消息转换器。

我们有什么办法可以实现这一目标吗?根据条件加载消息转换器?

EDIT

添加了我的 spring 配置文件

 <beans xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<bean id="moneySerializer" class="api.serialize.MoneySerializer"/>
    <bean id="moneyDeserializer" class="api.serialize.MoneyDeserializer"/>
    <bean id="serializationModule" class="api.serialize.SerializationModule">
        <constructor-arg index="0" ref="moneySerializer"/>
        <constructor-arg index="1" ref="moneyDeserializer"/>
    </bean>

    <bean id="customObjectMapper" class="api.serialize.CustomObjectMapper" primary="true">
        <constructor-arg ref="serializationModule"/>
    </bean>
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="api.serialize.ResponseJSONConverterCamelCaseToSnakeCase" >
                <constructor-arg ref="customObjectMapper"/>
            </bean>
            <bean class="api.serialize.ResponseJSONConverter">
                <constructor-arg ref="objectMapper"/>
            </bean>
        </mvc:message-converters>

    </mvc:annotation-driven>

    <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>

</beans>

EDIT 2.0

我的 servlet.xml

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="com.tgt.promotions.api.serialize.ServiceJSONConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>

自定义消息转换器

    public class ServiceJSONConverter extends MappingJackson2HttpMessageConverter {

    @Autowired
    public ServiceJSONConverter(SnakeCaseObjectMapper snakeCaseObjectMapper) {
        setObjectMapper(snakeCaseObjectMapper);
    }
}

自定义对象映射器

@Component
public class SnakeCaseObjectMapper extends ObjectMapper {
    @Autowired
    public SnakeCaseObjectMapper(PropertyNamingStrategy propertyNamingStrategy) {
        setSerializationInclusion(JsonInclude.Include.NON_NULL);
        setPropertyNamingStrategy(propertyNamingStrategy);
    }
}

自定义属性命名策略

@Component
public class CustomPropertyNamingStrategy extends PropertyNamingStrategy {

    @Autowired
    private HttpServletRequest request;

    private final PropertyNamingStrategy legacyStrategy  = PropertyNamingStrategy.LOWER_CASE;
    private final PropertyNamingStrategy defaultStrategy  = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES;


    @Override
    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
        return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName);
    }

    @Override
    public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
        return getStrategy().nameForField(config, field, defaultName);
    }

    @Override
    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        return getStrategy().nameForGetterMethod(config, method, defaultName);
    }

    @Override
    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
        return getStrategy().nameForSetterMethod(config, method, defaultName);
    }

    private PropertyNamingStrategy getStrategy() {
        if (isLegacyEndpoint(request)) {
            return legacyStrategy;
        } else {
            return defaultStrategy;
        }
    }

    private boolean isLegacyEndpoint(HttpServletRequest request) {
        return request != null && request.getRequestURL() != null && !request.getRequestURL().toString().contains("/v3");
    }
}

我建议创建一个自定义实现,而不是使用 2 个不同的对象映射器PropertyNamingStrategy,相应地使用另外 2 个策略:

public class AwesomePropertyNamingStrategy extends PropertyNamingStrategy {

  private PropertyNamingStrategy legacyStrategy  = PropertyNamingStrategy.LOWER_CASE;
  private PropertyNamingStrategy defaultStrategy  = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES;

  @Override
  public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
    return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName);
  }

  // TODO: implement other nameForXXX methods

  private PropertyNamingStrategy getStrategy() {
    if (isLegacyEndpoint()) {
      return legacyStrategy;
    } else {
      return defaultStrategy;
    }
  }

  private boolean isLegacyEndpoint() {
    // TODO: get hold of the RequestContext or some other thead-local context 
    // that allows you to know it's an old or a new endpoint
    return false;
  }
}

您应该想出一种在旧模式和新模式之间切换的方法:

  1. 通过以某种方式访问​​请求上下文来使用端点 URL
  2. 如果您的旧端点使用不同的响应对象,请使用正在转换的对象的类来确定遗留/正常或您自己的自定义@LegacyResponse而是对所有旧类进行注释。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何根据条件应用Spring消息转换器? 的相关文章

随机推荐

  • Safari (iPad) 中的弹性项目重叠

    我无法通过简单的任务解决问题 容器宽度为 100 但有 padding right 和 box sizing 溢出 容器是弹性行 容器有两个具有动态内容的子容器 第一个孩子有内容的大小 第二个孩子占据剩余的宽度 预期结果 iPad 结果 例
  • 我如何调试 Hadoop MapReduce [重复]

    这个问题在这里已经有答案了 我正在尝试构建一个地图缩减作业 它运行完成 但最后呈现奇怪的数据 当我尝试使用 system out println debug data 调试它时 它没有显示在屏幕上 使用 java API 生成外部日志文件
  • 标准容器的复杂性保证有哪些?

    显然 标准容器提供了某种形式的保证 有哪些类型的保证以及不同类型集装箱之间的具体区别是什么 工作自SGI页面 http www sgi com tech stl about STL http en wikipedia org wiki St
  • Android 在应用程序被杀死时重新安排 Alarmmanager 警报

    我开发了一个应用程序来安排多个本地通知以提醒用户做某事 今年的每个月都应该发出通知 这些本地通知是使用 AlarmManager 安排的 在 BroadcastReceiver 的 OnRetrieve 中创建并引发通知 它一切正常 直到应
  • 避免 Angular CLI 中的相对路径

    我正在使用最新的 Angular CLI 并且创建了一个自定义组件文件夹 它是所有组件的集合 例如 TextInputComponent has a TextInputConfiguration放置在里面的类src components c
  • 在 Windows 中对二进制文件进行逆向工程的最佳实践是什么? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 就我而言 它写成c c 如果这很重要的话 IDA 表现出色 IDA反汇编器 http www hex rays com idapro
  • 加密柱状转置密码

    我试图弄清楚如何在给定明文大写字符串和任意长度的数字密钥的情况下加密 Python 中的柱状转置密码 例如 如果键是 3124 并且字符串是 IHAVETWOCATS 它将像这样组织字符串 3124 IHAV ETWO CATS 然后先返回
  • 使用 aspnet_regiis 加密 web.config 部分会删除 system.webServer 元素

    我使用以下命令来加密 web config 中的自定义部分 C Windows Microsoft NET Framework64 v4 0 30319 aspnet regiis exe aspnet regiis exe pe Cust
  • 如何在shell脚本中操作数组

    我希望我的脚本定义一个空数组 如果预定义条件成立 则应添加数组值 为此我所做的是 declare a FILES file count 0 if file ext SUPPORTED FILE TYPE then echo file ext
  • 在控制台应用程序中获取 IP 地址

    我希望通过控制台应用程序找出我的 IP 地址 我习惯使用网络应用程序Request ServerVariables收集和 或Request UserHostAddress 如何在控制台应用程序中完成此操作 最简单的方法如下 using Sy
  • EF在运行时从Type获取记录列表

    目的 我需要循环所有记录 例如 var records db Set
  • 如何在 Rails 的功能测试中启用页面缓存?

    是否可以打开页面缓存进行功能测试 以下内容不起作用 class ArticlesControllerTest lt ActionController TestCase def setup ActionController Base publ
  • 是否可以获取对象的不可枚举继承属性名称?

    在 JavaScript 中 我们有几种获取对象属性的方法 具体取决于我们想要获取的内容 1 Object keys 它返回对象的所有自己的可枚举属性 即 ECMA5 方法 2 a for in循环 返回对象的所有可枚举属性 无论它们是自己
  • 你如何理解一大块代码?

    我是一名刚刚开始工作的应届大学毕业生 在我的起步阶段 我需要学习很多产品代码 有一些设计文档 但没有多大帮助 您能否提供一些通用技术来浏览和理解庞大的产品代码 特别是 C 运行它doxygen http www doxygen nl 这将生
  • StreamBuilder 子更新在导航后不呈现

    我正在使用扑动StreamBuilder决定向用户显示哪个 根 页面 现在基本上有2种可能性 LoginPage or HomePage 我的应用程序的主要构建方法如下所示 Widget build BuildContext context
  • 在没有 root 权限的 Linux 上安装 gcc

    我可以使用公共图书馆中的计算机 并且我想尝试一些 C 以及其他代码 问题是没有安装 g 并且我无法使用包安装它 因为我没有 root 访问权限 是否有一种 智能 方法可以在主文件夹中创建完整的编程环境 我安装了gcc 我可以编译C代码 另外
  • 错误:yarn start - 错误命令“start”未找到

    我正在尝试学习 React 并且我正在使用一个私人存储库来开始它 I run yarn start在存储库的目录中 但我收到错误消息 yarn run v1 13 0 error Command start not found info V
  • 简单对话框中的 Stackoverflow 异常

    您好 我在这两个对话框中收到 Stackoverflow 异常 Dialog A正在从主对话框类中调用 对话框A有一个选择去Dialog A child and Dialog A child有选择回去Dialog A 但它遇到了 Stack
  • 如何使用Javascript触发CSS“悬停状态”? [复制]

    这个问题在这里已经有答案了 当用户将鼠标悬停在元素上时 CSS 悬停状态 将触发 我们如何使用 Javascript 将元素设置为 悬停状态 是否可以 如果您可以接受使用 focus您可以使用以下命令来代替悬停 var links docu
  • 如何根据条件应用Spring消息转换器?

    我有一个控制器 其响应是驼峰式 json 值 现在我们正在用新版本重写代码 所需的响应位于snake case中 我添加了一个消息转换器并修改了对象映射器来设置setPropertyNamingStrategy PropertyNaming