Recently, I decided to stop using sessions and explore tokens for a more secure and robust approach, while also creating a unified interface for both browsers and mobile apps. I refactored a practice project's frontend to use Ajax entirely, keeping Spring MVC for view controller forwarding. This deepened my understanding of JSON data transmission, which I'd like to share and invite corrections.
Understanding JSON: Object vs. String
In JavaScript, we often define a JSON object like this:
var jsonObject = {
"username": "admin",
"password": 123
};
This is a JSON object. There is also a JSON string—a string enclosed in single or double quotes. Strings can be printed directly, but objects cannot. In JavaScript, we use two utility methods to convert between them:
JSON.parse(): Parses a JSON string into a JavaScript object.JSON.stringify(): Converts a JavaScript value to a JSON string.
For example:
var jsonObject = {
"username": "admin",
"password": 123
};
alert(jsonObject); // Shows [object Object]
alert(JSON.stringify(jsonObject)); // Shows {"username":"admin","password":123}
Sending JSON Data via Ajax
When sending JSON to Spring MVC via Ajax, should we send a JSON object or a JSON string? Let's experiment with sending a JSON object first:
var username = $("#username").val();
var password = $("#password").val();
var json = {
"username": username,
"password": password
};
$.ajax({
url: "jsontest",
type: "POST",
async: true,
data: json,
dataType: 'json',
success: function(data) {
if (data.userstatus === "success") {
$("#errorMsg").remove();
} else {
if ($("#errorMsg").length <= 0) {
$("form[name=loginForm]").append(errorMsg);
}
}
}
});
Using @RequestParam
Spring MVC provides @RequestParam, similar to request.getParameter() in Servlets. Let's try receiving parameters with it:
@RequestMapping("/jsontest")
public void test(@RequestParam(value = "username", required = true) String username,
@RequestParam(value = "password", required = true) String password) {
System.out.println("username: " + username);
System.out.println("password: " + password);
}
The backend successfully prints the parameters! Even without @RequestParam, Spring MVC automatically binds the parameters:
@RequestMapping("/jsontest")
public void test(String username, String password) {
System.out.println("username: " + username);
System.out.println("password: " + password);
}
This works because jQuery's Ajax, by default, uses the Content-Type: application/x-www-form-urlencoded encoding. This encoding serializes the JSON object into a query string like username="admin"&password=123 and sends it in the request body (for POST) or URL (for GET). @RequestParam and request.getParameter() can handle this format.
Using @RequestBody for JSON Strings
Spring MVC also provides @RequestBody for handling requests with content types other than application/x-www-form-urlencoded, such as application/json or application/xml. To use it, the Ajax request must change its Content-Type to application/json and send a JSON string (not an object):
$.ajax({
url: "jsontest",
type: "POST",
async: true,
contentType: "application/json",
data: JSON.stringify(json),
dataType: 'json',
success: function(data) {
if (data.userstatus === "success") {
$("#errorMsg").remove();
} else {
if ($("#errorMsg").length <= 0) {
$("form[name=loginForm]").append(errorMsg);
}
}
}
});
On the backend, we can receive the JSON string using @RequestBody with a Map:
@RequestMapping("/jsontest")
public void test(@RequestBody(required = true) Map<String, Object> map) {
String username = map.get("username").toString();
String password = map.get("password").toString();
System.out.println("username: " + username);
System.out.println("password: " + password);
}
If we omit @RequestBody, Spring MVC cannot parse the body, leading to a NullPointerException.
We can also bind directly to a POJO (Plain Old Java Object) using @RequestBody:
@RequestMapping("/jsontest")
public void test(@RequestBody User user) {
String username = user.getUsername();
String password = user.getPassword();
System.out.println("username: " + username);
System.out.println("password: " + password);
}
This works perfectly. However, for small data payloads like login credentials, I prefer extracting individual values from a Map rather than creating a dedicated User object if I don't need all fields.
Binding POJO with JSON Object
Can we bind a POJO when sending a JSON object (not a string)? Yes, but do not use @RequestParam. Without that annotation, Spring MVC can bind parameters directly to a POJO field:
@RequestMapping("/jsontest")
public void test(User user) {
// user will have username and password populated
}
Common Errors and Summary
- 415 Unsupported Media Type: Occurs if you use
@RequestBodybut the AjaxContent-Typeis stillapplication/x-www-form-urlencoded. Always setcontentType: "application/json"when using@RequestBody. - Backend log: Without logging framework, Tomcat may not show errors. Ensure you have proper logging configured.
Key Takeaways
Frontend Content-Type |
Data Format Sent | Backend Annotation |
|---|---|---|
application/x-www-form-urlencoded (default) |
JSON object (key-value pairs) | @RequestParam or no annotation for simple types; can bind to POJO without @RequestParam |
application/json |
JSON string | @RequestBody (for Map, POJO, etc.) |
Quick Reference
- JSON object →
@RequestParam(or ServletgetParameter) for simple fields. - JSON string →
@RequestBodyfor complex binding.
I hope this summary of my day-long experiments helps you! If there are any mistakes, please kindly point them out.