RestRepositoryController скрыть конечные точки репозитория REST - PullRequest
0 голосов
/ 12 июля 2020

Я использую Spring Boot 2.3.1 с SDR, HATEOAS, Hibernate. В моем проекте я открыл репозитории через Spring Data REST. Я уже использую SDR в другом проекте, но в этом у меня странная проблема с одним контроллером. Фактически, если я добавлю несколько пользовательских конечных точек в этот контроллер, все конечные точки по умолчанию для связанной сущности будут скрыты и больше не будут доступны.

Позвольте мне объяснить лучше. У меня есть этот объект:

@EntityListeners(TenantListener.class)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Tenant extends AbstractEntity {

    @Enumerated(value = EnumType.STRING)
    @Column(nullable = false, updatable = false)
    private TenantType type;

    @NotBlank
    @Column(nullable = false)
    private String fullName;

    @NotBlank
    @Column(nullable = false)
    private String lastName;

    @NotBlank
    @Column(nullable = false)
    private String firstName;

    @Username
    @Size(min = 4, max = 16)
    @Column(nullable = false, unique = true)
    @ColumnTransformer(write = "LOWER(?)")
    private String tenantId;

    //other fields

, и это Репозиторий:

@Transactional
@IsManagementUser
public interface TenantRepository extends JpaRepository<Tenant, Long>, JpaSpecificationExecutor {

    @Caching(evict = {
            @CacheEvict(value = "tenants", allEntries = true),
            @CacheEvict(value = "tenants#id", allEntries = true),
            @CacheEvict(value = "tenants#sid", allEntries = true),
            @CacheEvict(value = "tenants#exists", allEntries = true),
    })
    @Override
    <S extends Tenant> S save(S s);

    @Caching(evict = {
            @CacheEvict(value = "tenants", allEntries = true),
            @CacheEvict(value = "tenants#id", allEntries = true),
            @CacheEvict(value = "tenants#sid", allEntries = true),
            @CacheEvict(value = "tenants#exists", allEntries = true),
    })
    @Override
    void deleteById(Long aLong);

  
    @Caching(evict = {
            @CacheEvict(value = "tenants", allEntries = true),
            @CacheEvict(value = "tenants#id", allEntries = true),
            @CacheEvict(value = "tenants#sid", allEntries = true),
            @CacheEvict(value = "tenants#exists", allEntries = true),
    })
    @Modifying
    void deleteByTenantId(String tenantId);


  
    @Cacheable(cacheNames = "tenants#id")
    Optional<Tenant> findByTenantId(@Param("tenantId") String tenantId);

    @Cacheable(cacheNames = "tenants#sid")
    @Query("SELECT sid FROM Tenant t WHERE t.tenantId=:tenantId")
    String findSidByTenantId(@Param("tenantId") String tenantId);

  
    @Cacheable(cacheNames = "tenants#exists")
    @Query("SELECT case WHEN (COUNT(*) > 0)  THEN true ELSE false end FROM Tenant WHERE tenantId=:tenantId")
    boolean existsTenantId(@Param("tenantId") String tenantId);

    @Caching(evict = {
            @CacheEvict(value = "tenants", allEntries = true),
            @CacheEvict(value = "tenants#id", allEntries = true),
            @CacheEvict(value = "tenants#exists", allEntries = true),
    })
    @Modifying
    @Query("UPDATE Tenant t SET t.verified=:verified, t.version=t.version+1, t.lastModifiedDate=UTC_TIMESTAMP() WHERE t.tenantId=:tenantId")
    void setVerified(@Param("tenantId") String tenantId, @Param("verified") boolean verified);

    @Caching(evict = {
            @CacheEvict(value = "tenants", allEntries = true),
            @CacheEvict(value = "tenants#id", allEntries = true),
            @CacheEvict(value = "tenants#exists", allEntries = true),
    })
    @Modifying
    @Query("UPDATE Tenant t SET t.enabled=:enabled, t.version=t.version+1, t.lastModifiedDate=UTC_TIMESTAMP() WHERE t.tenantId=:tenantId")
    void setEnabled(@Param("tenantId") String tenantId, @Param("enabled") boolean enabled);
}

, и это контроллер REST:

@RepositoryRestController
@RequestMapping(path = "/api/v1")
@PreAuthorize("isAuthenticated()")
public class TenantController {

    @Autowired
    private LocalValidatorFactoryBean validator;

    @Autowired
    private TenantRepository tenantRepository;


    @Autowired
    private DbmsManager dbmsManager;

    @Autowired
    private PagedResourcesAssembler pagedResourcesAssembler;

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(validator);
    }


    @IsManagementUser
    @DeleteMapping(path = "/tenants/{id}")
    public ResponseEntity<?> deleteTenant(@PathVariable("id") Long id) {
        Optional<Tenant> optionalTenant = tenantRepository.findById(id);
        if (optionalTenant.isPresent()) {
            dbmsManager.dropTenant(optionalTenant.get().getTenantId());
        }
        return ResponseEntity.noContent().build();
    }

}

Если я вызываю конечную точку GET http://localhost/api/v1/tenants, Получаю весь список жильцов. Если я попытаюсь вызвать любой другой метод, например GET http://localhost/api/v1/tenants/1 или PATCH http://localhost/api/v1/tenants/1, я получаю предупреждение в консоли:

12/07/2020 21:51:01,440  WARN http-nio-9999-exec-2 PageNotFound:209 - Request method 'GET' not supported

Если я закомментирую все конечные точки в моем TenantController, все будет работать нормально. Кажется, что независимо от того, какую конечную точку я создаю в своем контроллере, все остальные конечные точки SDR по умолчанию скрываются.

Это происходит только с этим объектом и этим контроллером, но я не вижу ничего особенного. Любой намек действительно приветствуется, чтобы понять, в чем проблема.

1 Ответ

0 голосов
/ 13 июля 2020

Если вы хотите смешаться с пространством URI Spring Data REST, вы не должны использовать @RequestMapping на уровне типа. Если вы это сделаете, контроллер будет включен в сопоставление обработчика Spring MVC, а не в Spring Data REST. Для выбора обработчика это приводит к нахождению соответствия URI и Spring MVC, а затем только проверяет обработчики в этом конкретном сопоставлении для выбора нисходящего потока в отношении метода HTTP, создает и потребляет предложения et c.

К сожалению, это поведение было установлено в Spring MVC целую вечность и не может быть изменено, так как это нарушит существующие приложения, которые, зная или неосознанно, зависят от этого.

Я подал этот билет с помощью Spring MVC пересмотреть это (даже если только для 6.0).

Кстати. чтобы просто запустить некоторые бизнес-логики c на основе событий жизненного цикла агрегатов, см. также события, публикуемые Spring Data REST. Подробнее читайте в справочной документации . Они позволяют вообще не писать собственный контроллер в сценарии ios, подобном этому.

...