Spring Boot - 为AJAX请求重用REST控制器,表单数据序列化

我们正在使用API优先方法构建应用程序,我们只是为了开始构建REST API并在以后添加GUI .

现在,我正在尝试将GUI添加到某些模块中,而这就是我选择正确的前进方式所困扰的地方 .

我的Spring Boot Web App中有一个 RestController 方法来创建用户

@PostMapping(value={"/rest/admin/users", "/ajax/admin/users"})
    @ApiOperation(value="Create a user", produces="application/json", consumes="application/json")
    public ResponseEntity<User> create(@RequestBody User user) {
        User createdUser = this.userService.add(user);
        URI location = CommonFunctions.buildResourceLocation("/{id}", createdUser.getId());
        return ResponseEntity.created(location).body(createdUser);
    }

我们的REST API使用基本身份验证进行保护,API完美运行,没有任何问题 .

这里要注意的是:

  • 可以从 /rest/admin/users 以及 /ajax/admin/users/rest 表示REST调用和 /ajax 表示来自UI的AJAX调用)访问该方法 . 这是一种可接受的方法吗?

  • 通过 /rest URL进行的任何呼叫都需要具有基本身份验证标头,而 /ajax 呼叫不受基本身份验证保护 . 他们采用普通的表单登录身份验证 . 在这种情况下,他们只需要 X-CSRF 标头 .

现在,为了减少我们必须编写的代码,我只想重新使用上述REST控制器方法通过Ajax创建用户 .

在我的JSP中,我有一个类似下面的表单:

<form:form action="/ajax/admin/users" modelAttribute="user" id="user_create_form">
    <label for="name">Full Name</label>
    <input type='text' id='name' name='name' placeholder='' class='form-control focus-on-load'/>

    <label for="email">E-Mail Address</label>
    <input type='text' id='email' name='email' placeholder='' class='form-control'/>

    <label for="role">Role</label>
    <select class='form-control short' name='role.id' id='role'>
        <option value='2'>User</option>
        <option value='1'>Site Administrator</option>
    </select>
</form:form>

单击保存按钮,我正在序列化表单数据,以便我可以得到这样的JSON:

{
    "name":"User Full Name",
    "email":"useremail@somedomain.com",
    "role":{
        "id":1
    }
}

要序列化,我使用以下代码:

var formData = getFormData($form);

我从here获得 getFormData($form) 的逻辑

这是我的AJAX电话

$.ajax({
    url:$form.attr('action'),
    method:'POST',
    dataType:'json',
    contentType:'application/json',
    data:JSON.stringify(formData),
    beforeSend:function(xhr) {
        if (header && token) {
            xhr.setRequestHeader(header, token);
        }
    },
    success:function(result) {
         //Do something to indicate success
    }, error: function(xhr, textStatus, errorThrown) {
         //Do something to indicate failure
    }
});

这里的一切都很好,除了序列化 . 当我序列化表单数据时,这就是我得到的

{
    "name":"SOme Name",
    "email":"someemail@org.com",
    "role.id":1
}

而不是

{
    "name":"Some Name",
    "email":"someemail@org.com",
    "role":{"id":1}
}

这是阻止我重新使用我的REST控制器方法进行AJAX请求的唯一因素 . GET请求工作正常,没有问题 .

虽然我知道我可以在启动AJAX调用之前生成自定义JSON对象,但我想要一个通用的解决方案,因为会有很多屏幕,我想尽可能避免错误 .

这里更大的问题是,是否建议将REST控制器重用于AJAx请求(如果两者都有不同的身份验证领域/方案)?如果是这样,我是否采取了正确的方法?

谢谢,Sriram

回答(1)

2 years ago

您使用相同的API,其中一个请求具有身份验证,另一个请求未经身份验证(不推荐) .

在您第一次注意到的事情中,您定义了您为两个不同的目的重复使用相同的方法 .

我认为你试图实现的东西实际上违反了REST-API标准 . 因此,建议您使用两种不同的方法(使用不同方法的两个不同的URL)重写此api,其中一个 /ajax/admin/users 必须在匿名打开的URL中列出,而 /rest/admin/users 必须是安全的 .

据我所知,在Spring安全性中使用 csrf 令牌来验证客户端....因此,您必须在每次请求时发送令牌以验证/验证门户上的用户 .

现在来到第二点......(ajax调用序列化)

据我所知,你的html表单结构是扁平的(所有在相同的层次结构)

<label for="name">Full Name</label>
 <input type='text' id='name' name='name' placeholder='' class='form-control focus-on-load'/>

 <label for="email">E-Mail Address</label>
 <input type='text' id='email' name='email' placeholder='' class='form-control'/>

 <label for="role">Role</label>
 <select class='form-control short' name='role.id' id='role'>
     <option value='2'>User</option>
     <option value='1'>Site Administrator</option>
 </select>

如果你看到select的名字是 role.id ,你的html元素的存在形式是直的 . 所以,默认标准json将由ajax序列化生成,将是一个扁平的json之类

{
    "name":"SOme Name",
    "email":"someemail@org.com",
    "role.id":1
}

建议您编写自己的自定义方法,根据需要更改对象结构 .

谢谢...