Удаление ассоциации через REST в Spring Data REST+HATEOAS

Я хочу знать, как удалить связь "многие ко многим" с помощью вызова REST. Я могу создавать записи и связывать их, но не понимаю, как удалять.

У меня есть проект Spring Boot, в котором я использую REST и HATEOAS для обхода служб и контроллеров и прямого доступа к моему репозиторию.

У меня есть класс User Model/Domain

@Entity
@Table(name = "usr")
public class User implements Serializable {

private static final long serialVersionUID = 1L;

@Version
private long version = 0;

@Id
@GeneratedValue(generator="optimized-sequence")
private Long id;

@Column(nullable = false, unique = true, length = 500)
@Size(max = 500)
private String userName;

@Column(nullable = false, length = 500)
@Size(max = 500)
private String firstName;

@Column(nullable = false, length = 500)
@Size(max = 500)
private String lastName;

@ManyToMany(    fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable( name="user_role",
            joinColumns={ @JoinColumn(  name = "user_id", 
                                        nullable = false
                                    ) 
                        }, 
            inverseJoinColumns={ @JoinColumn(   name="role_id", 
                                                nullable=false
                                            ) 
                                }
)
private Set<Role> roles = new HashSet<Role>(0);

...Getters/Setters Below...

Как видите, у меня есть член roles, который является ассоциацией Many-To-Many с классом Role, код которого такой:

@Entity
public class Role {

@Id
@GeneratedValue(generator="optimized-sequence")
private Long id;

@Column(nullable = false)
private String name;

@Column(nullable = false)
private String description;

...Getters/Setters Below...

Мои репозитории выглядят так:

Пользовательский репозиторий

public interface UserRepository extends 
        JpaRepository<User, Long>, JpaSpecificationExecutor<User> {

    List<User> findByUserName(String username);

}

Репозиторий Ролей

public interface RoleRepository 
        extends JpaRepository<Role, Long> {

}

Теперь все хорошо. Когда я получаю доступ к корню проекта из браузера, я получаю индекс/каталог репозитория в формате JSON+HAL. Замечательный.

(Обратите внимание, что я удаляю часть http:// из приведенного ниже теста, потому что StackOverflow засчитывает ее в мою квоту на ссылки)

Я, используя REST-клиент WizTools, отправляю HTTP.POST в репозиторий ролей ( localhost:8080/resttest/roles ) и создаю новую роль. Успех, ID роли #4 создан.

Затем я отправляю POST в репозиторий пользователей, чтобы создать пользователя ( localhost:8080/resttest/users ). Успех, идентификатор пользователя № 7 создан.

Затем я помещаю в репозиторий пользователя, чтобы создать ассоциацию с ролью:

PUT localhost:8080/resttest/users/7/roles
Content-type: uri-list
Body: localhost:8080/resttest/roles/4

Здорово! Ассоциация сделана. Пользователь 9 теперь связан с ролью 4.

Теперь я не могу понять, как УДАЛИТЬ эту ассоциацию.

Я отправляю HTTP DELETE вместо PUT с той же командой, что и выше.

DELETE localhost:8080/resttest/users/7/roles
Content-type: uri-list
Body: localhost:8080/resttest/roles/4

Я получаю ответ: метод HTTP/1.1 405 не разрешен

{
    "timestamp":1424827169981,
    "status":405,
    "error":"Method Not  Allowed",
    "exception": "org.springframework.web.HttpRequestMethodNotSupportedException",
    "message":"Request method 'POST' not supported"
}

person BKaun    schedule 25.02.2015    source источник
comment
Подскажите, пожалуйста, какое решение!?   -  person erotsppa    schedule 25.11.2019


Ответы (2)


Хотя создание запроса PUT с оставшимися элементами помогает, DELETE является принятой командой для удаления ресурса ассоциации, и в большинстве случаев ее проще использовать.

Что касается вашего примера, это должно работать:

DELETE localhost:8080/resttest/users/7/roles/4

Отдельно стоит отметить, что при создании ассоциации ожидается наличие URI в полезной нагрузке. Вам не нужно писать весь URL в теле, этого должно быть достаточно:

PUT localhost:8080/resttest/users/7/roles
Content-type: uri-list
Body: /roles/4

Надеюсь это поможет.

person Anthony Drogon    schedule 24.11.2016
comment
Это неудобно, так как все остальные запросы (положить, опубликовать, получить) опираются на ссылки, а не на идентификаторы. Для выполнения удаления необходимо написать код для извлечения идентификатора из объекта _link. - person Giovanni Silva; 21.06.2017

Из документов:

УДАЛЯТЬ

...

405 Метод не разрешен — если ассоциация не является обязательной

PUT означает, что вы заменяете весь набор roles. Таким образом, чтобы удалить одну ссылку, вы PUT удалите все остальные ссылки. Если у вас есть только одна ссылка и вы хотите ее удалить, вы должны PUT создать пустую коллекцию:

PUT localhost:8080/resttest/users/7/roles
Content-type: uri-list
Body:

Кстати: вы не будете отправлять тело с запросом DELETE. Это не имеет смысла.

ИЗМЕНИТЬ

См. также этот ответ от разработчика Spring HATEOAS.

person a better oliver    schedule 25.02.2015
comment
Спасибо за ответ. Это многое объясняет. У меня два уточняющих вопроса. В ситуациях, когда ассоциации могут быть многочисленными, есть ли способ просто УДАЛИТЬ одну вместо ПОМЕЩЕНИЯ всего набора без того, который больше не нужен? Мой другой вопрос: что такое необязательная ассоциация? - person BKaun; 25.02.2015
comment
Я больше не могу редактировать комментарий. Просто хочу уточнить. Не говоря уже о том, что УДАЛИТЬ один, поскольку он должен быть методом УДАЛИТЬ http, это может быть любой метод, если он позволяет удалить только одну ассоциацию вместо всего набора без того, который вам нужен. Или мне нужно создать репозиторий для этой таблицы мостов? - person BKaun; 25.02.2015
comment
@BKaun Вы можете использовать POST для добавления одного URI, но нет удобного способа удалить один URI. Необязательное означает, что должно быть значение. Обычно это тот случай, когда столбец не nullable. Я предполагаю, что коллекция семантически всегда необязательна: она может быть пустой, но она всегда есть. - person a better oliver; 25.02.2015
comment
@BKaun stackoverflow.com/questions/28679934/ - person a better oliver; 25.02.2015
comment
Большое спасибо, ZeroflagL. Прежде чем писать здесь, я просмотрел все подряд, и ссылку, которую вы дали, я тоже видел. Конечно, поскольку в этой ветке не было ответов, я сдался, прочитав комментарии и ничего не найдя об удалении (взглянув на его комментарии, я заметил, что он говорил об PATCH/PUT, а я преследовал DELETE, поэтому я закрыл его в отчаянии) . Почти каждый пример curl показывает добавление и удаление ванили, а что касается ассоциаций, они показывают только добавление. Я так расстроилась, что пришлось написать здесь. Вы очень помогли. - person BKaun; 25.02.2015
comment
@BKaun такая же ситуация. Гипотетически, если у меня есть коллекция из 10 000 объектов, относящихся к моему домену, и пользователь удаляет 1, я должен отправить 9 999 URI. Кажется нелогичным. - person John Giotta; 18.03.2016
comment
Ответ Оливера Гирке касается удаления всех элементов списка. Чтобы удалить один элемент, лучше использовать команду DELETE. Это могло измениться почти за два года, поэтому я удаляю свой отрицательный голос. - person Anthony Drogon; 24.11.2016