获取LDAP单条记录时触发ConverterNotFoundException问题排查
你遇到的这个问题本质是Spring Data REST的转换服务没有识别到Spring LDAP自带的StringToNameConverter,导致在把URL路径里的DN(区分名)字符串转换成javax.naming.Name类型时找不到合适的转换器,触发了ConverterNotFoundException。下面给你几个实用的解决方案:
方案一:全局注册String到Name的转换器
最彻底的方式是把String转Name的转换器注册到Spring的全局转换服务里,让Spring Data REST能自动找到它。你可以创建一个配置类来实现:
import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.ldap.support.LdapUtils; import javax.naming.Name; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { // 注册自定义转换器:String -> Name registry.addConverter(String.class, Name.class, source -> LdapUtils.newLdapName(source)); } }
或者如果你更倾向于Spring Data REST的专属配置,也可以实现RepositoryRestConfigurer:
import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; import org.springframework.core.convert.converter.Converter; import org.springframework.ldap.support.LdapUtils; import javax.naming.Name; @Configuration public class RestRepositoryConfig implements RepositoryRestConfigurer { // 把转换器注册为Bean,让Spring自动识别 @Bean public Converter<String, Name> stringToNameConverter() { return LdapUtils::newLdapName; } }
方案二:在Repository中手动处理ID转换
如果不想全局注册转换器,也可以在你的LdapUserRepository里重写findById方法,手动把传入的String类型ID转换成Name:
import org.springframework.ldap.support.LdapUtils; import org.springframework.stereotype.Repository; import javax.naming.Name; import java.util.Optional; @Repository public interface LdapUserRepository extends LdapRepository<LdapUser> { LdapUser findByCommonName(String commonName); List<LdapUser> findByCommonNameContainingIgnoreCase(String commonName); // 重写findById,手动转换String到Name @Override default Optional<LdapUser> findById(String id) { Name dn = LdapUtils.newLdapName(id); return findById(dn); } }
不过要注意,Spring Data REST可能在调用这个方法前还是会尝试自动转换ID,所以这个方案有时候不如方案一稳定。
方案三:调整实体类的ID类型(可选)
如果你的项目允许调整实体类,也可以把@Id字段的类型从Name改成String,这样就不需要转换器了:
@Entry(objectClasses = {"inetOrgPerson", "organizationalPerson", "person", "top"}) @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class LdapUser { @Id private String distinguishedName; // 其他属性保持不变... // 可选:添加方法方便转换为Name类型,供LDAP操作使用 public Name getDnAsName() { return LdapUtils.newLdapName(distinguishedName); } }
这时候需要把Repository的泛型改成LdapRepository<LdapUser, String>,如果你的项目已经有很多基于Name的代码,这个方案的改动成本会比较高,所以优先考虑前两个方案。
验证效果
配置完成后重启应用,再访问/ldapUsers/cn=Katherine%20Ito,ou=Peons,应该就能成功返回单条用户记录了。
内容的提问来源于stack exchange,提问作者Sayak Mukhopadhyay

