Spring Data JPA无效的page.sort参数

9

在使用Hibernate的Spring Data JPA的Web应用程序中,我们利用网页分页功能,为各种实体列表提供分页和排序功能。

@Controller
public class MyEntityController {
   @RequestMapping(method = RequestMethod.GET)
   public ModelAndView list(Pageable pageable) { ... }
}

@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        argumentResolvers.add(new PageableArgumentResolver());
    }
}

public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> {
    Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable);
}

这使得实体属性可以在渲染的HTML中以特殊的请求参数形式进行定义,其中page.sort值实际上匹配实体中要排序的属性。
<table>
    <thead>
        <tr>
            <th><a href="?page.sort=propertyX&amp;page.sort.dir=asc">Property X</a></th>
            <th><a href="?page.sort=propertyY&amp;page.sort.dir=asc">Property Y</a></th>
        </tr>
    </thead>
    <tbody>...</tbody>
</table>

这将生成类似以下的URL:

这会生成一个类似于以下的URL:

http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc

问题在于用户可能会修改URL以使用无效的page.sort属性,这些属性引用不存在的列/属性名称,或者更糟糕的是,使用无效的JPA查询字符导致语法无效。

例如,如果将URL修改为按“noSuchProperty”进行排序:

http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc

但是如果该属性不存在,将会抛出以下异常:
java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity
    at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76)
     . . .
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86)
     . . .
    at $Proxy68.findByPropertyX(Unknown Source)
    at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17

同样地,如果将URL修改为无效的查询语法字符,例如 """:

http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc

以下错误将会发生:
java.lang.StackOverflowError
    java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
    . . .
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)

(还有一种异常情况是,当存储库方法上显式定义了@Query时,会导致一个org.hibernate.QueryException)。
Spring Data JPA抽象出了排序、分页和处理这些参数的细节,但它似乎不能优雅地处理这些场景(即指定无效的排序参数)。
我们可以增加一些额外的自定义逻辑来验证排序属性是否确实存在于实体中;然而,我想知道是否有更清晰、更集中的方法来做到这一点,这样我们就不会失去Spring Data JPA抽象所带来的好处和简洁性。我们在整个应用程序中使用这个排序功能,涉及许多不同的实体,因此理想情况下,我们希望更多地采用通用的方法,而不是必须为每个实体页面请求显式定义或检查排序属性。
具体而言,我们实际上扩展了PageableArgumentResolver,接受了一个注释的排序默认值,在我们的控制器中提供(出于简单起见,未在代码示例中说明),因此我们希望只回退到此默认排序顺序,或者只回退到实体的默认排序顺序,而不是抛出异常。
一些想法和尝试...我可以使用QueryCreationListener拦截查询创建,并获取排序参数;但是,在那一点上我无法修改查询。或者,我可以扩展和使用自定义的PageableArgumentResolver(我们已经在这样做了)来获取排序参数;但是,在那一点上,我没有访问实体的能力,也无法确定该实体是否确实具有该名称的属性。我们可以显式地声明支持的属性;然而,同样,这会破坏在不需要特定或声明的实体知识的情况下集中自动处理此场景的想法。
是否有任何其他类型的拦截器或类似的结构,我可以利用它们集中验证分页可排序参数并在调用查询之前进行必要的修改?或者是否有任何类型的配置或方式,Spring可以自动处理这种情况,使其更优雅地处理无效的排序参数?
2个回答

3
我看了一下代码,认为更多的堆栈跟踪信息会很有帮助。但从我所看到的情况来看,如果您想重写一些Spring代码,可能有两个地方需要解决。
这里有两种情况,第一种情况是您正在传递一个在对象/表中不存在的排序字段。您真正想要的是,无论何时都会默默忽略该错误参数,而不仅仅是在传递1 PageableArgumentResolver]1时才忽略。我认为应该在 AbstractQueryCreator(因此也包括 JpaQueryCreator)上设置一个选项,以忽略排序中的错误参数。
第二个需要解决的部分可能是PageableArgumentResolver。如果传递空字符串或类似于%20这样没有意义的内容,则应该忽略该参数,不将其发送到PageRequest
祝愉快编程,好运。阅读您的帖子让我意识到我的网站也面临同样的问题,而我真的没有好的解决方案。

1

我认为改进PageableArgumentResolver以优雅地处理这些情况是很有必要的。它可以尝试从作为Sort传递的String中创建一个PropertyPath实例,从而确保它是有效的。我有点犹豫,是否有意义默认丢弃无效的String,这将导致结果根本没有排序。这可能是最无缝的体验,但也可能导致乏味的尝试找出为什么结果没有排序。

然而,如果您能在Spring Data Commons上提出一个JIRA票据,并在此处链接该票据,那就太棒了。如果您已经完善了可行的实现,请随时打开拉取请求。感谢您已经将此带到讨论桌上!


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