diff --git a/spring-web-modules/spring-mvc-basics-5/pom.xml b/spring-web-modules/spring-mvc-basics-5/pom.xml
index 5e8cd1b44e..0f2b0bc7bd 100644
--- a/spring-web-modules/spring-mvc-basics-5/pom.xml
+++ b/spring-web-modules/spring-mvc-basics-5/pom.xml
@@ -38,6 +38,17 @@
spring-boot-starter-test
test
+
+ org.apache.commons
+ commons-io
+ 1.3.2
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.7.0
+
+
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/AddressDto.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/AddressDto.java
new file mode 100644
index 0000000000..b8bd8b9218
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/AddressDto.java
@@ -0,0 +1,55 @@
+package com.baeldung.jsonargs;
+
+public class AddressDto {
+
+ private String streetName;
+ private String streetNumber;
+ private String postalCode;
+ private String city;
+ private String country;
+
+ public String getStreetName() {
+ return streetName;
+ }
+
+ public void setStreetName(String streetName) {
+ this.streetName = streetName;
+ }
+
+ public String getStreetNumber() {
+ return streetNumber;
+ }
+
+ public void setStreetNumber(String streetNumber) {
+ this.streetNumber = streetNumber;
+ }
+
+ public String getPostalCode() {
+ return postalCode;
+ }
+
+ public void setPostalCode(String postalCode) {
+ this.postalCode = postalCode;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ @Override
+ public String toString() {
+ return "Address{" + "streetName='" + streetName + '\'' + ", streetNumber='" + streetNumber + '\'' + ", postalCode='" + postalCode + '\'' + ", city='" + city + '\'' + ", country='" + country + '\'' + '}';
+ }
+}
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArg.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArg.java
new file mode 100644
index 0000000000..1ee614982e
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArg.java
@@ -0,0 +1,12 @@
+package com.baeldung.jsonargs;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonArg {
+ String value() default "";
+}
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArgumentResolver.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArgumentResolver.java
new file mode 100644
index 0000000000..3cb01dae32
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/JsonArgumentResolver.java
@@ -0,0 +1,50 @@
+package com.baeldung.jsonargs;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.io.IOUtils;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import com.jayway.jsonpath.JsonPath;
+
+public class JsonArgumentResolver implements HandlerMethodArgumentResolver {
+
+ private static final String JSON_BODY_ATTRIBUTE = "JSON_REQUEST_BODY";
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.hasParameterAnnotation(JsonArg.class);
+ }
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ String body = getRequestBody(webRequest);
+ String jsonPath = Objects.requireNonNull(Objects.requireNonNull(parameter.getParameterAnnotation(JsonArg.class))
+ .value());
+ Class> parameterType = parameter.getParameterType();
+ return JsonPath.parse(body)
+ .read(jsonPath, parameterType);
+ }
+
+ private String getRequestBody(NativeWebRequest webRequest) {
+ HttpServletRequest servletRequest = Objects.requireNonNull(webRequest.getNativeRequest(HttpServletRequest.class));
+ String jsonBody = (String) servletRequest.getAttribute(JSON_BODY_ATTRIBUTE);
+ if (jsonBody == null) {
+ try {
+ jsonBody = IOUtils.toString(servletRequest.getInputStream());
+ servletRequest.setAttribute(JSON_BODY_ATTRIBUTE, jsonBody);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return jsonBody;
+ }
+}
+
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserController.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserController.java
new file mode 100644
index 0000000000..eb1abbb675
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserController.java
@@ -0,0 +1,33 @@
+package com.baeldung.jsonargs;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+@RequestMapping("/user")
+public class UserController {
+
+ @PostMapping("/process/custom")
+ public ResponseEntity process(@JsonArg("firstName") String firstName, @JsonArg("address.city") String city) {
+ /* business processing */
+ return ResponseEntity.ok()
+ .body(String.format("{\"firstName\": %s, \"city\" : %s}", firstName, city));
+ }
+
+ @PostMapping("/process")
+ public ResponseEntity process(@RequestBody UserDto user) {
+ /* business processing */
+ return ResponseEntity.ok()
+ .body(user.toString());
+ }
+
+ @PostMapping("/process/custompojo")
+ public ResponseEntity process(@JsonArg("firstName") String firstName, @JsonArg("lastName") String lastName, @JsonArg("address") AddressDto address) {
+ /* business processing */
+ return ResponseEntity.ok()
+ .body(String.format("{\"firstName\": %s, \"lastName\": %s, \"address\" : %s}", firstName, lastName, address));
+ }
+}
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserDto.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserDto.java
new file mode 100644
index 0000000000..55ac683ebc
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/jsonargs/UserDto.java
@@ -0,0 +1,46 @@
+package com.baeldung.jsonargs;
+
+public class UserDto {
+
+ private String firstName;
+ private String lastName;
+ private String age;
+ private AddressDto address;
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getAge() {
+ return age;
+ }
+
+ public void setAge(String age) {
+ this.age = age;
+ }
+
+ public AddressDto getAddress() {
+ return address;
+ }
+
+ public void setAddress(AddressDto address) {
+ this.address = address;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age='" + age + '\'' + ", address=" + address + '}';
+ }
+}
diff --git a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/spring/web/config/WebConfig.java b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/spring/web/config/WebConfig.java
index 53865d02d5..a1b7d469c1 100644
--- a/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/spring/web/config/WebConfig.java
+++ b/spring-web-modules/spring-mvc-basics-5/src/main/java/com/baeldung/spring/web/config/WebConfig.java
@@ -1,7 +1,10 @@
package com.baeldung.spring.web.config;
+import java.util.List;
+
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
@@ -11,18 +14,26 @@ import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
+import com.baeldung.jsonargs.JsonArgumentResolver;
+
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
- public void addViewControllers(final ViewControllerRegistry registry) {
+ public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/")
.setViewName("index");
}
+ @Override
+ public void addArgumentResolvers(List argumentResolvers) {
+ JsonArgumentResolver jsonArgumentResolver = new JsonArgumentResolver();
+ argumentResolvers.add(jsonArgumentResolver);
+ }
+
@Bean
public ViewResolver viewResolver() {
- final InternalResourceViewResolver bean = new InternalResourceViewResolver();
+ InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".jsp");
@@ -41,9 +52,10 @@ public class WebConfig implements WebMvcConfigurer {
}
@Bean
- public BeanNameViewResolver beanNameViewResolver(){
+ public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver beanNameViewResolver = new BeanNameViewResolver();
beanNameViewResolver.setOrder(1);
return beanNameViewResolver;
}
+
}
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/jsonargs/UserControllerIntegrationTest.java b/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/jsonargs/UserControllerIntegrationTest.java
new file mode 100644
index 0000000000..acb0214e35
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-5/src/test/java/com/baeldung/jsonargs/UserControllerIntegrationTest.java
@@ -0,0 +1,52 @@
+package com.baeldung.jsonargs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@WebMvcTest(controllers = UserController.class)
+class UserControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ void whenSendingAPostJSON_thenReturnFirstNameAndCity() throws Exception {
+ String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Smith\",\"age\":10,\"address\":{\"streetName\":\"Example Street\",\"streetNumber\":\"10A\",\"postalCode\":\"1QW34\",\"city\":\"Timisoara\",\"country\":\"Romania\"}}";
+ mockMvc.perform(post("/user/process/custom").content(jsonString)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(MockMvcResultMatchers.jsonPath("$.firstName")
+ .value("John"))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.city")
+ .value("Timisoara"));
+ }
+
+ @Test
+ void whenSendingAPostJSON_thenReturnUserAndAddress() throws Exception {
+ String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Smith\",\"address\":{\"streetName\":\"Example Street\",\"streetNumber\":\"10A\",\"postalCode\":\"1QW34\",\"city\":\"Timisoara\",\"country\":\"Romania\"}}";
+ ObjectMapper mapper = new ObjectMapper();
+ UserDto user = mapper.readValue(jsonString, UserDto.class);
+ AddressDto address = user.getAddress();
+
+ String mvcResult = mockMvc.perform(post("/user/process/custompojo").content(jsonString)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertEquals(String.format("{\"firstName\": %s, \"lastName\": %s, \"address\" : %s}", user.getFirstName(), user.getLastName(), address), mvcResult);
+ }
+}
\ No newline at end of file