Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I make HTTP POST request from Thymeleaf table in Spring Boot application

I have a Thymeleaf template in a simple Spring Boot application. The template contains a list in a table as follows:

<p>There are <span th:text="${#lists.size(persons)}"></span> people:</p>
<table th:if="${not #lists.isEmpty(persons)}" border="1">
    <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Address</th>
        <th>Telephone</th>
        <th>Email</th>
        <th>Actions</th>
    </tr>
    <tr th:each="person : ${persons}">
        <td th:text="${person.personId}"></td>
        <td th:text="${person.name}"></td>
        <td th:text="${person.address}"></td>
        <td th:text="${person.telephone}"></td>
        <td th:text="${person.email}"></td>
        <td>
            <a href="#" data-th-href="@{/edit(personId=${person.personId})}">Edit</a> |
            <a href="#" data-th-href="@{/delete(personId=${person.personId})}">Delete</a>
        </td>
    </tr>
</table>

I want to enable edit and delete functionality as per the last cell in the table. But at the moment both requests are for HTTP GET. That is fine for edit where a person's details are fetched from the server for editing, but delete should trigger a POST request because of the data changes on the server.

Does anyone know if Thymeleaf allow a POST request per row of a table? Or do I have to write a simple HTML form per row?

The GET form is currently:

<td>
    <a href="#" data-th-href="@{/edit(personId=${person.personId})}">Edit</a>
    <!--a href="#" data-th-href="@{/delete(personId=${person.personId})}">Delete</a></td-->
    <form method="get" th:action="@{/edit(personId=${person.personId})}">
        <button type="submit" name="submit" value="value">Edit</button>
    </form>
</td>

Where I have a link and a form for testing.

The controller method to be called is:

// Gets a Person.
@RequestMapping(value="/edit", method=RequestMethod.GET)
public String getEditPerson(@RequestParam("personId") String personId, Model model) {
    logger.info(PersonController.class.getName() + ".getEditPerson() method called."); 

    Person person = personDAO.get(Integer.parseInt(personId));
    model.addAttribute("person", person);

    // Set view.      
    return "/edit";
}    

The error when the button version of GET is called is:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Jul 24 00:26:16 BST 2016
There was an unexpected error (type=Bad Request, status=400).
Required String parameter 'personId' is not present`

I am using GET to trigger editing because no data is sent to the server here other than the personId. No database action is taken so it should be a GET.

like image 787
Mr Morgan Avatar asked Jul 23 '16 20:07

Mr Morgan


People also ask

Which is better JSP or Thymeleaf?

Thymeleaf is way better in my opinion because it have good underlying priciples and exploits natural behaviour of browsers. Jsp makes html hard to read, it becomes weird mixture of html and java code which makes a lot of problems in comunication between designer - developer.

Is Thymeleaf front end or backend?

Thymeleaf is server rendered, it's not front end.


2 Answers

you are using Links and I don't think that is possible, you would need to use a form where you can specify the method POST to be used.

In the example below im using a <button> instead of a <a> element, but it will work, the only thing you need to do is to style your button with CSS to look like your links

<form method="POST" th:action="@{/edit(personId=${person.personId})}">
    <button type="submit" name="submit" value="value" class="link-button">This is a link that sends a POST request</button>
</form> 

now in your code should look like this

<tr th:each="person : ${persons}">                
    <td th:text="${person.personId}"></td>
    <td th:text="${person.name}"></td>
    <td th:text="${person.address}"></td>
    <td th:text="${person.telephone}"></td>
    <td th:text="${person.email}"></td>
    <td>
        <form method="POST" th:action="@{/edit(personId=${person.personId})}">
            <button type="submit" name="submit" value="value" class="link-button">EDIT</button>
        </form> | 
        <form method="POST" th:action="@{/delete(personId=${person.personId})}">
            <button type="submit" name="submit" value="value" class="link-button">DELETE</button>
        </form>
    </td>
</tr>

EDIT

As you just shared you Java code, in the controller you are expecting the personId not as a PathVariable, but as a RequestParam, in that case your form should have that value...

edit your form and add the person id as follows.

<form method="POST" th:action="@{/edit}">
    <input type="hidden" name="personid" id="personId" th:value="${person.personId}" />
    <button type="submit" name="submit" value="value" class="link-button">This is a link that sends a POST request</button>
</form> 

Notice also I changed the action of the form to be just /edit, as its what your controller looks like

like image 179
Rayweb_on Avatar answered Oct 20 '22 13:10

Rayweb_on


Does anyone know if Thymeleaf allow a POST request per row of a table? Or do I have to write a simple HTML form per row?

HTML doesn't support POST request with links and you have to use forms (as Rayweb_on explained). But Thymeleaf allows you to define custom tags which helps a lot :

<a th:href="@{/edit(personId=${person.personId})}" custom:linkMethod="post">Edit</a>

... which would generate following HTML (assuming jQuery is available) :

<a href="#" onclick="$('<form action=&quot;/edit/personId=123456&quot; method=&quot;post&quot;></form>').appendTo('body').submit(); return false;">Edit</a>

Custom tag definition (without error checking to keep it simple) :

/**
 * Custom attribute processor that allows to specify which method (get or post) is used on a standard link.
 */
public class LinkMethodAttrProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTR_NAME = "linkMethod";
    private static final int PRECEDENCE = 10000;

    public LinkMethodAttrProcessor(final String dialectPrefix) {
        super(
                TemplateMode.HTML, // This processor will apply only to HTML mode
                dialectPrefix,     // Prefix to be applied to name for matching
                null,              // No tag name: match any tag name
                false,             // No prefix to be applied to tag name
                ATTR_NAME,         // Name of the attribute that will be matched
                true,              // Apply dialect prefix to attribute name
                PRECEDENCE,        // Precedence (inside dialect's own precedence)
                true);             // Remove the matched attribute afterwards
    }

    @Override
    protected void doProcess(final ITemplateContext context, final IProcessableElementTag tag,
                             final AttributeName attributeName, final String attributeValue,
                             final IElementTagStructureHandler structureHandler) {

        // get the method name (tag parameter)
        final IEngineConfiguration configuration = context.getConfiguration();
        final IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);
        final IStandardExpression expression = parser.parseExpression(context, attributeValue);
        final String method = (String) expression.execute(context);

        // add custom javascript to change link method
        final String link = tag.getAttribute("href").getValue();
        final String action = "$('<form action=&quot;" + link + "&quot; method=&quot;" + method + "&quot;></form>').appendTo('body').submit(); return false;";
        structureHandler.setAttribute("onclick", action);
        structureHandler.setAttribute("href", "#");
    }
}

See the Thymelead documentation for example of how this custom attribute needs to be registered.

like image 33
Siggen Avatar answered Oct 20 '22 14:10

Siggen