0

We are building application using an API-first approach, where we are building REST APIs only to start with and add the GUI later.

Now, I'm trying to add GUI to some of the modules, and that's where I'm kind of stuck with choosing the right way to move forward.

I have a RestController method in my Spring Boot Web App to create a User

    @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);
    } 

Our REST APIs are secured with Basic Authentication, and the APIs work perfectly without any issues.

Things to note here are:

  1. The method can be accessed from /rest/admin/users as well as from /ajax/admin/users (/rest indicating a REST call and /ajax indicating an AJAX call from the UI). Is this an acceptable approach?
  2. Any calls via the /rest URL needs to have a Basic Auth Header, while /ajax calls are not secured with Basic auth. They take the normal form login authentication instead. In this case, they just require the X-CSRF headers.

Now, to reduce the code we have to write, I just want to re-use the aforementioned REST controller method to create a user via Ajax.

In my JSP, I have a form like the one below:

<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>

On click of the save button, I'm serializing the form data, so that I can get a JSON like this:

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

To serialize, I use the following code:

var formData = getFormData($form);

I got the logic for getFormData($form) from here

This is my AJAX call

$.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
    }
});

Here everything works fine, except for the serialization. When I serialize my form data, this is what I get

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

as opposed to

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

This is the only thing stopping me from re-using my REST controller methods for AJAX requests. GET requests work fine without issues.

While I know I can generate a custom JSON object before I fire the AJAX call, I want a general solution as there are going to be many screens and I want to avoid mistakes as much as possible.

The larger question here is, is it advisable to re-use REST controllers for AJAx requests (provided both have different authentication realms / schemes)? If so, am I following the right approach?

Thanks, Sriram

Sriram Sridharan
  • 720
  • 18
  • 43

1 Answers1

0
  1. You are using same API with one request with authentication and another without authentication (Not Recommended).

In your first noted thing you defined that you are re-using the same method for two different purposes.

I think the thing you try to achieve actually violates the REST-API Standard. So, suggest you to rewrite this api with two different methods (two different url with different methods), where one /ajax/admin/users must be listed in anonymous open url, while /rest/admin/users must be a secured one.

As far as i know csrf token used in spring security to validate client.... so, you must send your token at each request to validate/authenticate the User on portal.

  1. Now come to the second Point... (ajax call Serialization)

As far as i see that your html form structure is flat (all at same heirarchy level )

 <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>

if you see name of the select is role.id and your existence of html elements is straight parallel in form. So, default standard json will be generated by ajax serialisation will be a flat json like

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

Recommend you to write your own custom method to change the object structure as per your need.

Thank You...

Vikrant Kashyap
  • 6,398
  • 3
  • 32
  • 52
  • Thank you @Vikrant, however, I would like to point out that the /ajax URL is not public. Meaning, the /ajax will still throw a 401 error if there is no authentication. While the /rest needs basic authentication, the /ajax requests take the authentication principal from the current session's SecurityContextHolder, much the same way the other restricted URLs do. Does this pose a security threat? – Sriram Sridharan Dec 10 '18 at 12:24
  • And the only reason I want to re-use my Rest Controller methods is to avoid duplication of code. I can write different controller methods if required, but I was just exploring the possibility of re-using. – Sriram Sridharan Dec 10 '18 at 12:25