@DeleteMapping - nie działa

0

Cześć

Od razu zaznaczam, że jestem świeżakiem, prosiłbym o wyrozumiałość :) Mam problem z DeleteMapping. Od razu zaznaczam, że szukałem i kopiowałem rozwiązania z internetu i nic się nie zmienia.

Mianowicie uczę się i tworzę aplikację z nauczycielami i studentami. No i miałem zrobić tak, aby na liście lekcji, przy każdej lekcji, był przycisk i usuwał lekcję.
Zrobiłem skrypt w JSie, uruchamiany w przycisku:

function deleteLesson(lessonId) {
        var url = "/lessons?delete=" + lessonId;
        $.getJSON(url, setTimeout(function(){ location.reload(); }, 500));
    }

I od strony Backend'u:

@GetMapping(params = "delete")
    @ResponseBody
    public String returnAfterDelete(@RequestParam("delete") int lessonId) {
        lessonService.delete(lessonId);
        return "redirect:/lessons";
    }

i to działa. Ale jak chciałem zrobić z DeleteMapping to nie działa:

@DeleteMapping("?delete={lessonId}")
    public void deleteLesson(@PathVariable int lessonId) {
        lessonService.delete(lessonId);
    }

Nie ma żadnego błędu, po prostu jakby przez ten Mapping nie przechodziło.
Jak to poprawnie napisać? Bo już nie mam pojęcia co zrobić, próbowałem wszystkiego xd

1

Na necie o tej metodzie jquery która uzywasz pisze tak:
Description: Load JSON-encoded data from the server using a GET HTTP request.
Widzisz? Wysylasz request typu GET, a Spring spodziewa się DELETE. Dlatego nie działa.

1

No przeciez robisz HTTP GET z tego swojego jQuery. Musisz wywolac metode DELETE.

2

Dodatkowo definicja tego parametru dziwnie wygląda. Patrząc na jquery (potencjalnie jakbyś wołał tam delete) to powinno być:

@DeleteMapping
public void deleteLesson(@RequestParam int delete) {
    lessonService.delete(delete);
}

A tak naprawdę to pewnie chciałbyś zrobić coś takiego

@DeleteMapping("/{lessonId}")
public void deleteLesson(@PathParam int lessonId) {
    lessonService.delete(lessonId);
}

+

function deleteLesson(lessonId) {
    $.deleteJSON(`/lessons/$lessonId`, setTimeout(function(){ location.reload(); }, 500));
}

0

@Cyrec, @Charles_Ray, @VeloxDigitis dzięki wielkie chłopaki! Przyznaję, że dopiero poznaję całą resztę prócz Javy, a tym bardziej nic się prawie nie znam na html'u i JSie, ale na potrzeby zadania potrzebowałem to zrobić, a Springa będę musiał jednak umieć.

edit: Panowie, ale to w takim razie jak to wykonać, bo nie ma takiej metody jak $.deleteJSON(), a jak próbowałem z

function deleteLesson(lessonId) {
       var url = "/lessons/delete/" + lessonId;
       $.ajax({
           url: url,
           type: 'DELETE',
       });
   }

mając:

@DeleteMapping("/delete/{lessonId}")
   public void deleteLesson(@PathParam("id") int lessonId) {
       lessonService.delete(lessonId);
   }

to mi wyskakuje, że nie ma metody typu GET.

0

@kilroy:

Wołasz URLa: var url = "/lessons/delete/" + lessonId;,

a mapujesz:

@DeleteMapping("/delete/{lessonId}")
  1. Zobacz z @DeleteMapping("/lessons/delete/{lessonId}"),
  2. bez sensu używać w URL "/delete/" skoro mapujesz DELETE. Masło maślane.
0

@yarel:

  1. Mapuję tak, bo cała klasa kontrolera jest zmapowana pod @RequestMapping("/lessons), więc dlatego tak. Dla pewności sprawdziłem i wtedy wyskakuje błąd typu 404, że nie ma mapowania. A tak to mam błąd 500.
  2. To tak sobie sam dodałem, abym póki co wiedział czy okej, ale dzięki za wskazówkę, zmienię to.

Mam ewentualnie jeden pomysł, ale nie znam się to się spytam
Cały mój kod html wygląda tak:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>

    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

</head>

<body onload="populate()">

<h1>Lekcje</h1>
<td><button onclick="window.location.href='/students'">Lista studentów</button></td>
<td><button onclick="window.location.href='/teachers'">Lista nauczycieli</button></td>
<br>
<br>
<a href="/lessons/create" style="color: red; size: 30px">Dodaj lekcje</a>
<table>
    <thead>
    <tr>
        <th>ID-Lekcji</th>
        <th>Nauczyciel</th>
        <th>Student</th>
        <th>Data lekcji</th>
        <th></th>
    </tr>
    </thead>
    <tbody id="tbody">

    </tbody>
</table>

<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>


</body>

</html>

<script type="text/javascript">

    function deleteLesson(lessonId) {
        var url = "/lessons/delete/" + lessonId;
        $.ajax({
            type: 'DELETE',
            url: url,
        });
    }

    function populate() {
        var tab = document.getElementById("tbody");
        tab.innerHTML = '';
        var url = "/lessons/all"
        $.getJSON(url, function (data) {
            data.forEach(lesson => {
                var l1 = '<tr><td>';
                var l2 = lesson.id + '</td><td>';
                var l3 = lesson.teacher + '</td><td>';
                var l4 = lesson.student + '</td><td>';
                var l5 = lesson.date + '</td><td>';
                var l6 = '<button class="btn btn-danger btn-sm" id="button' + lesson.id + '" value="' + lesson.id + '" onclick="deleteLesson(this.value)">Usun</button>';
                var l8 = '</td></tr>';
                tab.innerHTML += (l1+l2+l3+l4+l5+l6+l8);
            })
        })
    }

</script>

I pytanie czy to nie jest kwestia tego, że ten button jest wrzucony w $.getJSON?
Czy nie ma to powiązania i to zupełnie co innego?
Jeśli tak to, gdzie leży błąd?

0

Ten kod jest fatalny. Nie wiem, czy mozna sobie dynamicznie wygenerowac DOM w ten sposob, raczej uzylbym jakiegos szablonu, np. https://stackoverflow.com/questions/9966093/how-to-iterate-over-an-array-of-objects-with-jquery-templates-each-command/10350276

A w ogole to przerzucilbym sie na Reacta, Angulara albo jakis Thymeleaf, zeby zrobic to w cywilizowany sposob.

Po drugie, nie uzywaj buttonow do nawigacji - od tego są linki.

Po trzecie, sprawdz w Network w przegladarce dokladnie jaki request XHR jest wykonywany do backendu.

0

@Charles_Ray:

  1. Pierw było to napisane typowo w Thymeleaf'ie, ale ze względu na problem wyodrębnieniem id do użycia w buttonie to pomysł padł taki. z @GetMapping działa, ale z delete już niestety nie, a chciałem, aby to było zgodne ze sztuką.
  2. Dzięki, potem to zmienię :)
  3. Poniżej wrzucam
DELETE http://localhost:8080/lessons/9 500        jquery.min.js:2
send @ jquery.min.js:2
ajax @ jquery.min.js:2
deleteLesson @ lessons:46
onclick @ lessons:1

No i info jest, że nie ma Get Mappingu.

A odnośnie wersji w Thymeleaf to poniżej przykład dla teachera

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>


</head>
<body style="background-color: white;">

<h1>Nauczyciele</h1>
<td><button onclick="window.location.href='/lessons'">Lista lekcji</button></td>
<td><button onclick="window.location.href='/students'">Lista studentów</button></td>
<br>
<br>
<h3><a href="/teachers/create">Dodaj Nauczyciela</a></h3>
<table style="solid; align-content: left; border: 2px solid black;">
    <thead>
    <tr>
        <th> ID</th>
        <th> Imie</th>
        <th> Nazwisko</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="teacher : ${teachers}">
        <td th:text="${teacher.getId()}"></td>
        <td th:text="${teacher.getFirstName()}"></td>
        <td th:text="${teacher.getLastName()}"></td>
    </tr>
    </tbody>
</table>

</body>

</html>
0

Error 500 sugeruje, ze mapping istnieje, ale wywalil sie serwer. Sprawdz logi.

0

@Charles_Ray: Przepraszam, w XHR jest 500. Jak go otworzę to sam error jest typu 405. Poniżej cytat:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Mar 06 18:07:27 CET 2022
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported

@yarel: poniżej cała klasa jak wygląda

package com.masters.coding.lesson;

import com.masters.coding.lesson.model.Lesson;
import com.masters.coding.lesson.model.LessonDto;
import com.masters.coding.student.StudentService;
import com.masters.coding.teacher.TeacherService;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/lessons")
@RequiredArgsConstructor
public class LessonController {
    private final LessonService lessonService;
    private final StudentService studentService;
    private final TeacherService teacherService;

    @GetMapping
    public String getLessonList(Model model) {
        model.addAttribute("lessons", lessonService.findAll());
        return "lesson/list";
    }

    @GetMapping("/all")
    @ResponseBody
    public List<LessonDto> getAllLessons() {
        return lessonService.findAll().stream()
                .map(LessonDto::fromEntity)
                .collect(Collectors.toList());
    }

//    @GetMapping(params = "delete")
//    @ResponseBody
//    public void returnAfterDelete(@RequestParam("delete") int lessonId) {
//        lessonService.delete(lessonId);
//    }

    @DeleteMapping("/delete/{lessonId}")
    public void deleteLesson(@PathVariable("lessonId") int lessonId) {
        lessonService.delete(lessonId);
    }

    @GetMapping("/create")
    public String getLessonCreateForm(Model model) {
        model.addAttribute("teachers", teacherService.findAll());
        model.addAttribute("students", studentService.findAll());
        return "lesson/create";
    }

    @PostMapping("/create")
    public String createLesson(Lesson lesson,
                               @RequestParam("studentId") int studentId) throws Exception {
        lessonService.save(lesson, studentId);
        return "redirect:/lessons";
    }

    @GetMapping(params = "studentId")
    @ResponseBody
    public List<LessonDto> findStudentLessons(@RequestParam("studentId") int studentId) {
        return lessonService.getStudentLessons(studentId).stream()
                .map(LessonDto::fromEntity)
                .collect(Collectors.toList());
    }

    @GetMapping("/changeDate")
    public String getLessonChangeDateForm(Model model) {
        model.addAttribute("lessons", lessonService.findAll());
        model.addAttribute("students", studentService.findAll());
        return "lesson/changeDate";
    }

    @PostMapping("/changeDate")
    public String changeLessonDate(@RequestParam("lessonId") int lessonId, @RequestParam("date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) throws Exception {
        lessonService.changeDate(lessonId, date);
        return "redirect:/lessons";
    }

}

to zakomentowane to po małej zmianie w html'u to normalnie działa.

1

No i gdzie jest ten mapping dla DELETE /lessons/9?

0

var url = "/lessons/delete/" + lessonId;

Url nie powonien być na wzor "http://localhost:8080/lessons/delete/" + lessonId?

1

Na pewno poczytaj trochę o RESTful API, bo te ścieżki delete albo create trochę nie mają sensu. Właśnie metody HTTP są po to, żeby rozróżniać akcje.

Robisz zapytanie DELETE /lessons/1, a Twój serwer ma zdefiniowane DELETE /lessons/delete/1

0

@Charles_Ray, @VeloxDigitis: w Kontrolerze jest @DeleteMapping("/delete/{lessonId}"), gdzie cały kontroler jest zmapowany pod @RequestMapping("/lessons"), więc łącznie wychodzi ten url /lessons/delete/{lessonId}",a w html'u jest var url = "/lessons/delete/" + lessonId;.

To tak samo jak mam @GetMapping("/create"), który z tego względu, że cały kontroler jest zmapowany pod @RequestMapping("/lessons") to i w przeglądarce i gdziekolwiek idzie wychodzi /lessons/create i to normalnie działa. Tak samo jak z changeDate i całą resztą. Mapowanie jest okej. Z resztą powtarzam - sprawdzałem to i jeśli zmienię to mówi, że nie ma w ogóle takiego mapowania, a nie, że jest problem z metodą get.

0

Chłopie dlaczego bierzesz się za front gdy samo API jest niedopracowane? Swoje endpointy możesz testować postmanem, wtedy wyeliminujesz zastanawianie się czy problem leży po stronie frontu czy backendu.

Najpierw zaimplementuj wszystkie metody w API tak aby działały zgodnie z Twoimi oczekiwaniami, a dopiero potem baw się w dopisywanie do tego frontu. I przestań to robić w thymeleafie...

0
kilroy napisał(a):

a w html'u jest var url = "/lessons/delete/" + lessonId;.

Pare postow temu pokazywales loga z konsoli

DELETE http://localhost:8080/lessons/9 500

To ja juz nie wiem jak jest 🤷🏻‍♂️

1

@kilroy: a dlaczego dla void nie zwracasz statusu operacji? W tutorialu nie było nic o @ResponseStatus ?

@DeleteMapping("/delete/{lessonId}")
    public void deleteLesson(@PathVariable("lessonId") int lessonId) {
        lessonService.delete(lessonId);
    }
0

@Charles_Ray: sorry, bo już próbuję wszystkiego i nic. Aktualnie zmieniłem tyle co nazwy i jest tak, że usuwa, ale i tak XHR wyskakuje błąd, w postmanie to samo. Podaję jak jest aktualnie i co wyskakuje:

Mapping na strone bez zmian: @RequestMapping("/lessons")
Mapping do metody:

@DeleteMapping("/{lessonId}")
    public void deleteLesson(@PathVariable("lessonId") int lessonId) {
        lessonService.delete(lessonId);
    }

funkcja w html'u:

function deleteLesson(lessonId) {
        var url = "/lessons/" + lessonId;
        $.ajax({
            type: 'DELETE',
            url: url,
        });
    }

błąd XHR:

jquery.min.js:2 
 DELETE http://localhost:8080/lessons/16 500
send	@	jquery.min.js:2
ajax	@	jquery.min.js:2
deleteLesson	@	lessons:46
onclick	@	lessons:1

jak otworzę błąd na stronie to:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback
Sun Mar 06 20:37:11 CET 2022
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported

A w Postmanie wysyłane zapytanie DELETE, na adres: localhost:8080/lessons/15

"timestamp": "2022-03-06T19:32:36.074+00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "org.thymeleaf.exceptions.TemplateInputException: Error resolving template [lessons/16], template might not exist or might not be accessible by any of the configured Template Resolvers\r\n\tat org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869)\r\n\tat

2

Hmm a dodaj do tej metody w kontrolerze @ResponseBody

Moze byc problem zwiazany z tym, ze kontroler „nie jest restowy” - naglowki, te sprawy. Mozesz tez dac @RestController na calej klasie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1