Search code examples
spring-bootspring-mvcspring-data-jpathymeleaf

I can't edit an existing object using Spring + Thymeleaf


I'm trying to edit an existing object in db but logger says that the method is not allowed.

Here is the template:

<html xmlns:th="http://www.thymeleaf.org">
    <body>
        <form th:action="@{save}" th:object="${opera}" method="POST">
             <div class="input-container">
                 <div>
                    <div>Name:</div> 
                    <div><input type="text" th:field="*{nome}"></div>
                 </div>
                        
                 <div><button type="submit">Save</button></div>
            </div>
        </form>
    </body>
</html>

Here is the handler method to edit the object:

@RequestMapping(value="/save", method=RequestMethod.POST)
public String saveOpera(@ModelAttribute("opera") Opera opera, Model model){
        
    this.operaService.inserisci(opera);
    return "opere.html";
}

And here is the handler method which return the edit page:

@RequestMapping(value="/edit/{id}", method=RequestMethod.GET)
public String showEditOpera(@PathVariable(name = "id") Long id, Model model) {
    model.addAttribute("opera", operaService.searchOperaById(id));
    return "/admin/saveProva";
}

And the stack trace if needed:

[2m2021-08-02 12:52:17.733[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m GET "/edit/15", parameters={}
[2m2021-08-02 12:52:17.734[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36ms.w.s.m.m.a.RequestMappingHandlerMapping[0;39m [2m:[0;39m Mapped to it.uniroma3.siw.controller.OperaController#showEditOpera(Long, Model)
[2m2021-08-02 12:52:17.736[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36morg.hibernate.SQL                       [0;39m [2m:[0;39m select opera0_.id as id1_4_0_, opera0_.artista_id as artista_4_4_0_, opera0_.collezione_id as collezio5_4_0_, opera0_.nome as nome2_4_0_, opera0_.path as path3_4_0_, artista1_.id as id1_0_1_, artista1_.biografia as biografi2_0_1_, artista1_.cognome as cognome3_0_1_, artista1_.data_di_morte as data_di_4_0_1_, artista1_.data_di_nascita as data_di_5_0_1_, artista1_.luogo_di_morte as luogo_di6_0_1_, artista1_.luogo_di_nascita as luogo_di7_0_1_, artista1_.nome as nome8_0_1_, artista1_.path as path9_0_1_, collezione2_.id as id1_1_2_, collezione2_.curatore_id as curatore4_1_2_, collezione2_.descrizione as descrizi2_1_2_, collezione2_.nome as nome3_1_2_, curatore3_.id as id1_3_3_, curatore3_.cognome as cognome2_3_3_, curatore3_.data_di_nascita as data_di_3_3_3_, curatore3_.nome as nome4_3_3_ from opera opera0_ left outer join artista artista1_ on opera0_.artista_id=artista1_.id left outer join collezione collezione2_ on opera0_.collezione_id=collezione2_.id left outer join curatore curatore3_ on collezione2_.curatore_id=curatore3_.id where opera0_.id=?
Hibernate: select opera0_.id as id1_4_0_, opera0_.artista_id as artista_4_4_0_, opera0_.collezione_id as collezio5_4_0_, opera0_.nome as nome2_4_0_, opera0_.path as path3_4_0_, artista1_.id as id1_0_1_, artista1_.biografia as biografi2_0_1_, artista1_.cognome as cognome3_0_1_, artista1_.data_di_morte as data_di_4_0_1_, artista1_.data_di_nascita as data_di_5_0_1_, artista1_.luogo_di_morte as luogo_di6_0_1_, artista1_.luogo_di_nascita as luogo_di7_0_1_, artista1_.nome as nome8_0_1_, artista1_.path as path9_0_1_, collezione2_.id as id1_1_2_, collezione2_.curatore_id as curatore4_1_2_, collezione2_.descrizione as descrizi2_1_2_, collezione2_.nome as nome3_1_2_, curatore3_.id as id1_3_3_, curatore3_.cognome as cognome2_3_3_, curatore3_.data_di_nascita as data_di_3_3_3_, curatore3_.nome as nome4_3_3_ from opera opera0_ left outer join artista artista1_ on opera0_.artista_id=artista1_.id left outer join collezione collezione2_ on opera0_.collezione_id=collezione2_.id left outer join curatore curatore3_ on collezione2_.curatore_id=curatore3_.id where opera0_.id=?
[2m2021-08-02 12:52:17.737[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicBinder     [0;39m [2m:[0;39m binding parameter [1] as [BIGINT] - [15]
[2m2021-08-02 12:52:17.739[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([id1_0_1_] : [BIGINT]) - [null]
[2m2021-08-02 12:52:17.739[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([id1_1_2_] : [BIGINT]) - [null]
[2m2021-08-02 12:52:17.740[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([id1_3_3_] : [BIGINT]) - [null]
[2m2021-08-02 12:52:17.740[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([artista_4_4_0_] : [BIGINT]) - [null]
[2m2021-08-02 12:52:17.740[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([collezio5_4_0_] : [BIGINT]) - [null]
[2m2021-08-02 12:52:17.740[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([nome2_4_0_] : [VARCHAR]) - [rap]
[2m2021-08-02 12:52:17.741[0;39m [32mTRACE[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.h.type.descriptor.sql.BasicExtractor  [0;39m [2m:[0;39m extracted value ([path3_4_0_] : [VARCHAR]) - [null]
[2m2021-08-02 12:52:17.743[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.s.w.s.v.ContentNegotiatingViewResolver[0;39m [2m:[0;39m Selected 'text/html' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
[2m2021-08-02 12:52:17.752[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-1][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Completed 200 OK
[2m2021-08-02 12:52:17.765[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-3][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m GET "/css/style.css", parameters={}
[2m2021-08-02 12:52:17.766[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-3][0;39m [36mo.s.w.s.handler.SimpleUrlHandlerMapping [0;39m [2m:[0;39m Mapped to ResourceHttpRequestHandler [Classpath [META-INF/resources/], Classpath [resources/], Classpath [static/], Classpath [public/], ServletContext [/]]
[2m2021-08-02 12:52:17.769[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-3][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Completed 200 OK
[2m2021-08-02 12:52:17.773[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-5][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m GET "/img/edificio.jpg", parameters={}
[2m2021-08-02 12:52:17.774[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-5][0;39m [36mo.s.w.s.handler.SimpleUrlHandlerMapping [0;39m [2m:[0;39m Mapped to ResourceHttpRequestHandler [Classpath [META-INF/resources/], Classpath [resources/], Classpath [static/], Classpath [public/], ServletContext [/]]
[2m2021-08-02 12:52:17.786[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-5][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Completed 200 OK
[2m2021-08-02 12:52:28.482[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m POST "/edit/save", parameters={masked}
[2m2021-08-02 12:52:28.483[0;39m [33m WARN[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36m.w.s.m.s.DefaultHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
[2m2021-08-02 12:52:28.483[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Completed 405 METHOD_NOT_ALLOWED
[2m2021-08-02 12:52:28.484[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m "ERROR" dispatch for POST "/error", parameters={masked}
[2m2021-08-02 12:52:28.485[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36ms.w.s.m.m.a.RequestMappingHandlerMapping[0;39m [2m:[0;39m Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
[2m2021-08-02 12:52:28.489[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36mo.s.w.s.v.ContentNegotiatingViewResolver[0;39m [2m:[0;39m Selected 'text/html' given [text/html, text/html;q=0.8]
[2m2021-08-02 12:52:28.492[0;39m [32mDEBUG[0;39m [35m17652[0;39m [2m---[0;39m [2m[nio-8090-exec-2][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Exiting from "ERROR" dispatch, status 405

Someone knows how to fix it? (I couldn't find similar question, so i just hope it's not just a missing comma or something like that.)


Solution

  • The following two messages within the logs give a clue about what's happening:

    POST "/edit/save", parameters={masked}
    Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
    

    As you can see, you're trying to call /edit/save in stead of /save. This happens because the form action attribute uses a relative path in stead of an absolute one.

    To solve this, prepend a slash to the form action within the template, eg. th:action="@{/save}":

    <!-- ... -->
    <form th:action="@{/save}" th:object="${opera}" method="POST">
      <!-- ... -->
    </form>
    <!-- ... -->