0

I have set up a Controller with two methods. The URL mappings are exactly the same, and the only difference is the mapping annotation method. One is @PostMapping and the other @DeleteMapping written in that order. However, when I try to call the @DeleteMapping method, the @PostMapping method is called.

Note that even though variables in the two URL forms are different, they have the same values. They are just from different HTML pages.

The question is how to call the desired method each time.

@PostMapping method

@Secured({"ROLE_ADMIN", "ROLE_STUDENT"})
@PostMapping("students/{username}/internships/{id}")
public String addInternship(Model model, @PathVariable("username") String username, @PathVariable("id") int id) {

    /* DOES SOME STUFF
    Student student = userService.getUser(username).getStudent();
    Internship internship = userService.getInternship(id);

    StudentInternship studentInternship = new StudentInternship(internship, student, "Sent");

    internship.setNumberOfPositions(internship.getNumberOfPositions() - 1);
    userService.updateInternship(internship);

    student.setApplicationNumber(student.getApplicationNumber() + 1);
    userService.updateStudent(student);

    userService.addStudentInternship(studentInternship);
    */

    return "redirect:/internships";
}

@DeleteMapping method

@Secured({"ROLE_ADMIN", "ROLE_STUDENT"})
@DeleteMapping("students/{username}/internships/{id}")
public String removeInternship(Model model, @PathVariable("username") String username, @PathVariable("id") int id) {

    /* DOES SOME STUFF
    Student student = userService.getUser(username).getStudent();
    Internship internship = userService.getInternship(id);

    int studentInternshipID = userService.getStudentInternshipByParams(student, internship).getId();

    internship.setNumberOfPositions(internship.getNumberOfPositions() + 1);
    userService.updateInternship(internship);

    student.setApplicationNumber(student.getApplicationNumber() - 1);
    userService.updateStudent(student);

    userService.removeStudentInternship(studentInternshipID);
    */

    return "redirect:/students/" + username + "/internships";
}

HTML

<form:form action="${pageContext.request.contextPath}/students/${username}/internships/${tempInternship.id}" method="POST">
    <input type="submit" value="Request" ${disabled}/>
</form:form>

<form:form action="${pageContext.request.contextPath}/students/${tempStudentInternship.student.username}/internships/${tempStudentInternship.internship.id}" method="DELETE">
    <input type="submit" value="Dismiss" />
</form:form>
Alexander Petrov
  • 9,204
  • 31
  • 70
Theroc
  • 3
  • 1

2 Answers2

2

Browsers only support GET and POST as http request methods. The solution is to send your form with the POST method and inject a hidden field inside the same html form called _method with your desired method as a value, in your case here, it is just DELETE. For the case of POST, just write your form as usual.

Example :

<form:form action="${pageContext.request.contextPath}/students/${tempStudentInternship.student.username}/internships/${tempStudentInternship.internship.id}" method="POST">
    <input type="hidden" name="_method" value="DELETE"/>
    <input type="submit" value="Dismiss" />
</form:form>

Please, have a look at this answer for creating the spring bean and then applying the mentioned form attribute inside spring:form html forms.

HPH
  • 388
  • 2
  • 11
  • What you don't say though is that it is framework dependent and does not come out of the box. So he still needs something to actualy make this work and this something comes in the form of included javascript again:) – Alexander Petrov Feb 03 '19 at 23:07
  • yes the OP is already developing the app in spring MVC and there is a built-in servlet filter intercepting this special form attribute `_method`, replacing the the request verb with its value. – HPH Feb 04 '19 at 08:34
0

Only GET and POST are allowed from a FORM. You need to use AJAX to specify additional types of the request. Hi you need to use javascript and XMLHttpRequest in order to specify the type of the request.

Here is one example I took randomly from internet:

// Delete a user
var url = "http://localhost:8080/api/v1/users";
var xhr = new XMLHttpRequest();
xhr.open("DELETE", url+'/12', true);
xhr.onload = function () {
    var users = JSON.parse(xhr.responseText);
    if (xhr.readyState == 4 && xhr.status == "200") {
        console.table(users);
    } else {
        console.error(users);
    }
}
xhr.send(null);
Alexander Petrov
  • 9,204
  • 31
  • 70
  • The thing is I do not want to use Javascript. I read that the Spring tags I am using support those extra methods, but I did not completely understand them so I was wondering if I could go about that way somehow. – Theroc Feb 02 '19 at 12:41
  • HTML 5 does not have support for form method DELETE. You need to figure out an alternative method to HTML 5 and javascript. Good luck :) – Alexander Petrov Feb 03 '19 at 22:57
  • By the way, the solution below posted by the HPH collegue is also javascript, just you will not directly invoke it. It will not work out of the box without including apropriate javascript framework supporting the method override. – Alexander Petrov Feb 03 '19 at 23:08
  • @AlexandarPetrov, i think you are missing a point here, the spring MVC servlet filter intercepting the _method form attribute is on the server side, there is no javascript thing (direct or indirect use) in my solution. – HPH Feb 04 '19 at 08:38
  • @HPH i am not missing the point :) The point is that there is a third party framework somewhere independently if it is Spring MVC or JQuery or Playframework that makes sure that the _method is converted to Delete. Put request. This is not a functionality that comes straight out of the box. Is it that important if the interceptor is within the browser or within the servlet container. – Alexander Petrov Feb 04 '19 at 09:39