当前位置: 首页 > 知识库问答 >
问题:

Thymeleaf,Spring MVC缺少表单提交上的表单支持对象:null

锺离鸿
2023-03-14

基本上,表单提交的表单支持对象总是空的。有人知道为什么会这样吗?

我有一个简单的控制器,显示设备列表。可以选择设备并对其应用功能。这是控制器:

@Controller
@RequestMapping
public class DeviceListController {

    private static final Logger LOG = LoggerFactory.getLogger(DeviceListController.class);

    @Autowired
        DeviceService deviceService;

    @RequestMapping("/devices/list")
    public String getDevicesPage(ModelMap model) {
        int page = 0;
        int size = 10;

        if(model.get("deviceCommand") != null) {
            DeviceCommand cmd = (DeviceCommand) model.get("deviceCommand");
            page = cmd.getPage();
            size = cmd.getSize();
        }
        Pageable pageRequest = new PageRequest(page, size);
        Page<Device> devices = deviceService.findAllPaginated(pageRequest);

        model.addAttribute("devices", DeviceMapper.map(devices));
        model.addAttribute("deviceCommand", new DeviceCommand(page, size));
        return "devices";
    }

    @RequestMapping("/devices/modify")
    public String modifyDevices(ModelMap model) {

        LOG.debug("Trying to get the device command {}.", model.get("deviceCommand"));

        if(model.get("deviceCommand") != null) {
            DeviceCommand cmd = (DeviceCommand) model.get("deviceCommand");

            LOG.debug("Processing directives {} and {} for {}.", cmd.getNewVersion(),cmd.getNewCommand(),cmd.getDeviceModificationIds());

        }
        return getDevicesPage(model);
    }
}

设备模型对象是一个Hibernate实体:

@Entity //TODO:  no connection between device and device details!
@Table(name="device")
public class Device {
    @Id
    @GeneratedValue
    private Long id;

    /**
     * This is an external representation of the device, and the ID for which the device is most
     * commonly searched for. The deviceID is a common reference point, used in T2 and in device reporting
     * on the client side.
     */
    @Column(name="device_id")
    private String deviceId;

    /**
     * A reference to the store number or store identification. Stores may have more than one device.
     */
    @Column(name="retailer_id")
    private String retailerId;

    /**
     * The name of the store where the device is situated.
     */
    @Column(name="retailer_name")
    private String retailerName;

    /**
     * The current version of the client software, which launches the browser. This is only applicable to integrated solutions
     */
    @Column(name="current_client_version")
    private String currentClientVersion;

    /**
     * The target version of the client software, which launches the browser. This is only applicable to integrated solutions
     */
    @Column(name="next_client_version")
    private String nextClientVersion;

    /**
     * Commands to be performed on the client, used most often to "clear the cache". As soon as hte se commands are run, they
     * are cleared from this column.
     */
    @Column(name="commands")
    private String commands;


    @Column(name = "language")
    private String language;

    /**
     * The signature is the client's position in the marketplace and used by other entities
     * to determine their applicability to the device.
     * RETAILER_GROUP:Tobaccoland|CHANNEL:Logistik|LOCALE:de-AT|INDUSTRY:5499
     * The * notation means any, the locale is using the standard locale code (up to 10 characters)
     * and the industry uses the merchant category codes, which in this case is
     * Miscellaneous Food Stores - Convenience Stores and Specialty Markets
     */
    @Column(name = "signature")
    private String signature;  //TODO: normalise?

    /**
     * This is the traceId that the device is currently up to
     */
    @Column(name = "trace_id")
    private Long traceId;
    /**
     * This is a tracing number that will be generated by touchpoint on each request.
     */
    @Column(name = "last_trace_id")
    private Long lastTraceId;

    /**
     * This is the transaction number that will come from Mercury in each response.
     */
    @Column(name = "last_transaction_id")
    private Long lastTransactionId;

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name="device_role",
        joinColumns = {@JoinColumn(name="device_id", referencedColumnName="id")},
        inverseJoinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")}
    )
    private List<Role> roles;

    @ManyToMany(cascade= CascadeType.ALL, mappedBy = "devices", fetch = FetchType.LAZY)
    private Set<User> users;

    // getters, setters, equals, hashcode, omitted
}

并通过DeviceMapper映射到DeviceViewModel

DeviceViewModel:

public class DeviceViewModel {

    private String id;
    private String retailerName;
    private String currentClientVersion;
    private String nextClientVersion;
    private String commands;
    private boolean shallModify;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getRetailerName() {
        return retailerName;
    }

    public void setRetailerName(String retailerName) {
        this.retailerName = retailerName;
    }

    public String getCurrentClientVersion() {
        return currentClientVersion;
    }

    public void setCurrentClientVersion(String currentClientVersion) {
        this.currentClientVersion = currentClientVersion;
    }

    public String getNextClientVersion() {
        return nextClientVersion;
    }

    public void setNextClientVersion(String nextClientVersion) {
        this.nextClientVersion = nextClientVersion;
    }

    public String getCommands() {
        return commands;
    }

    public void setCommands(String commands) {
        this.commands = commands;
    }

    public boolean isShallModify() {
        return shallModify;
    }

    public void setShallModify(boolean shallModify) {
        this.shallModify = shallModify;
    }
}

以及设备标签:

public class DeviceMapper {

    public static DeviceViewModel map(Device device) {
        DeviceViewModel dto = new DeviceViewModel( );
        dto.setId( device.getDeviceId() );
        dto.setRetailerName( device.getRetailerName() );
        dto.setCurrentClientVersion( device.getCurrentClientVersion() );
        dto.setNextClientVersion( device.getNextClientVersion() );
        dto.setCommands( device.getCommands() );
        return dto;
    }

    public static List<DeviceViewModel> map(Page<Device> devices) {
        List<DeviceViewModel> dtos = new ArrayList<>();

        for(Device device : devices) {
            dtos.add( map( device ) );
        }

        return dtos;
    }
}

现在是DeviceCommand,它是我的表单支持对象:

public class DeviceCommand {

    private List<String> deviceModificationIds;
    private String newVersion;
    private String newCommand;
    private int page;
    private int size;

    public DeviceCommand() {}

    public DeviceCommand(int page, int size) {
        this.page = page;
        this.size = size;
    }

    public List<String> getDeviceModificationIds() {
        return deviceModificationIds;
    }

    public void setDeviceModificationIds(List<String> deviceModificationIds) {
        this.deviceModificationIds = deviceModificationIds;
    }

    public String getNewVersion() {
        return newVersion;
    }

    public void setNewVersion(String newVersion) {
        this.newVersion = newVersion;
    }

    public String getNewCommand() {
        return newCommand;
    }

    public void setNewCommand(String newCommand) {
        this.newCommand = newCommand;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}

最后是devices.html页面的相关部分:

<form action="#" th:action="@{/devices/modify}" th:object="${deviceCommand}" method="post">
<table class="box-table-a">
    <h1 th:text="#{device.table.caption}">Site Users</h1>
    <thead>
        <tr>
            <th scope="col" th:text="#{device.check.label}">Select</th>
            <th scope="col" th:text="#{device.id.label}">(<span th:text="${device.retailer.name.label}"></span>)</th>
            <th scope="col" th:text="#{device.current.label}">Curr Version</th>
            <th scope="col" th:text="#{device.next.label}">Next Version</th>
            <th scope="col" th:text="#{device.commands.label}">Commands</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="d : ${devices}">
            <td><input type="checkbox" th:field="*{deviceModificationIds}" th:value="${d.id}"/></td>
            <td th:text="${d.id}">(<span th:text="${d.retailerName}"></span>)</td>
            <td th:text="${d.currentClientVersion}">Washington</td>
            <td th:text="${d.nextClientVersion}">gwash</td>
            <td th:text="${d.commands}">gwash</td>
        </tr>
        <tr>
        <td colspan="2">

            </td>
            <td th:text="#{device.change.version.label}"></td>
            <td th:text="#{device.add.command.label}"></td>
            <td></td>
        </tr>
        <tr>
        <td colspan="2">

            </td>
            <td><input type="text" th:field="*{newVersion}"/></td>
            <td><input type="text" th:field="*{newCommand}"/></td>
            <td><button type="submit" th:text="#{device.modify.action.button}">Action</button></td>
        </tr>
    </tbody>
</table>
</form>

虽然表单操作确实到达控制器中的modifyDevices方法,但deviceCommand表单支持对象为null。

为什么会这样?

共有1个答案

齐泰
2023-03-14

只需将类型为DeviceViewModel参数添加到您的modifyDevices方法
中,如下所示

@RequestMapping("/devices/modify")
public String modifyDevices(ModelMap model, DeviceViewModel deviceCommand ) {
/* access the submitted object as you want ....*/
}

您可能会发现这样一个问题:如何使用thymeleaf传递两个对象以在表单中使用?有用的

 类似资料:
  • 在Web开发中对于这样的一个流程可能很眼熟: 打开一个网页显示出表单。 用户填写并提交了表单。 如果用户提交了一些无效的信息,或者可能漏掉了一个必填项,表单将会连同用户的数据和错误问题的描述信息返回。 用户再次填写,继续上一步过程,直到提交了一个有效的表单。 在接收端,脚本必须: 检查用户递交的表单数据。 验证数据是否为正确的类型,合适的标准。例如,如果一个用户名被提交,它必须被验证是否只包含了允

  • 我正在尝试为我的列表模式中的每个对象创建一个包含该对象数据的表单。 所以我的控制器中有: 在我的ThymeLeaf模板中: 控制器: ThymeLeaf模板: 这种情况不会引发任何异常,但为空

  • 大家要切记这一点: 在任何 Single Page App中,js代码都不会产生 一个传统意义的form表单提交!(这会引起整个页面的刷新) 所以,我们往往用事件来实现.(桌面开发思维) 假设,我们在远程有个接口,可以接受别人的留言: URL: http://siwei.me/interface/blogs/add_comment 参数: content: 留言的内容. 请求方式: POST 返回

  • 获取Form变量 通过Action的如下方法可以获取变量: GetSlice GetString GetInt GetBool GetFloat GetFile GetForm 自动映射 通常我们通过http.Request.Form来获得从用户中请求到服务器端的数据,这些数据一般都是采用name,value的形式提交的。xweb默认支持自动将这些变量映射到Action的成员中,并且这种映射支持子

  • 问题内容: 我是stackoverflow和CodeIgniter的新手,目前正在尝试在Internet上找到的一些简单代码示例,以开始使用。我现在正在处理的是一种使用CI和Ajax(jQuery)的表单,并将表单的输入保存在数据库中,然后在表单的同一页上显示最新的表单。如果我感到困惑,那是这里的4.7应用示例。最初的源代码位于此处,但为了与最新版本的CI配合使用,我对其进行了修改,并在下面引用了

  • 我有一个表单,它显示了DataTables中的几个对象列表。通过此表单,管理员用户可以定义特定用户对这些对象的权限。 例如,这是product permissions dataTable jsp代码: 其中一些列表内部有数百或数千个对象,因此当我提交表单时,我得到spring bindexception: Org.SpringFramework.Validation.Bindexception:o