Possible to modify #ModelAttribute before #Validated is run - spring-mvc

Is is possible to modify a #ModelAttribute before it is validated via #Validated.
ie
#RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public final ModelAndView save(
#Validated(value = {myGroup.class}) #ModelAttribute("myObject") MyObject myObject)
I need to change the state of myObject before #Validated is executed

What about add a ModelAttribute populate method?
#ModelAttribute("myObject")
public MyObject modifyBeforeValidate(
#ModelAttribute("myObject") MyObject myObject) {
//modify it here
return myObject;
}
The side affect is this method will be invoked before every #RequestMapping method if I'm not mistaken.
Update1: example
#ModelAttribute("command")
public ChangeOrderCommand fillinUser(
#ModelAttribute("command") ChangeOrderCommand command,
HttpServletRequest request) {
command.setUser(securityGateway.getUserFrom(request));
return command;
}
#RequestMapping(value = "/foo/bar", method = RequestMethod.POST)
public String change(#ModelAttribute("command") ChangeOrderCommand command,
BindingResult bindingResult, Model model, Locale locale) {
}

There are 2 ways to modify the model attribute object before the #Validated will trigger:
Remove #Validated and autowire the validator and manually trigger the validator:
class MyController {
private final Validator validator;
class MyController(Validator validator) {
this.validator = validator;
}
#PostMapping("/doSomething")
public final ModelAndView save(
#ModelAttribute("myObject") MyObject myObject, BindingResult result) {
// edit MyObject here
validator.validate(myObject, result)
// original method body here
}
Decorate the default validator and pre-process the myObject object inside the decorated validator.
class MyController {
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(new PreProcessMyObjectValidator(binder.getValidator()));
}
#PostMapping("/doSomething")
public final ModelAndView save(
#Validated(value = {myGroup.class}) #ModelAttribute("myObject") MyObject myObject, BindingResult result) {
...
}
private static class PreProcessMyObjectValidator implements Validator {
private final Validator validator;
public PreProcessMyObjectValidator(Validator validator) {
this.validator = validator;
}
#Override
public boolean supports(#Nonnull Class<?> clazz) {
return validator.supports(clazz);
}
#Override
public void validate(#Nonnull Object target, #Nonnull Errors errors) {
if (target instanceof MyObject) {
MyObject myObject = (MyObject) target;
// manipulate myObject here
}
validator.validate(target, errors);
}
}
}
(This second tip is what I picked up from https://github.com/spring-projects/spring-framework/issues/11103)

Related

How to preserve HttpServletRequest autowiring when testing with SpringMVC Mockito and PowerMockito

Hi I'm trying to write some tests with Mockito and PowerMockito (I need to mock private methods) for a rest service written with SpringMVC and I'm facing the following issue
This is the semplified version of the controller
#Controller
#RequestMapping(value = "/test")
public class SimpleController {
#Autowired
private HttpServletRequest httpRequest;
#RequestMapping(value = "/simpleservice", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
#ResponseBody
public SimpleServiceResponse simpleService(#RequestBody SimpleServiceRequest simpleServiceRequest, HttpServletRequest httpServletRequest) {
SimpleServiceResponse simpleServiceResponse=new SimpleServiceResponse(simpleServiceRequest.getValue());
httpRequest.getHeader("Header");
return simpleServiceResponse;
}
}
and this is the correspoding test class
#WebAppConfiguration
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WebApplicationContext.xml","classpath:SimpleApplicationContext.xml"})
#PrepareForTest(WebController.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class TestSimpleControllerMockito {
private Logger logger = LoggerFactory.getLogger(TestSimpleControllerMockito.class.getName());
private ObjectMapper objectMapper= new ObjectMapper();
#InjectMocks
private SimpleController controller;
#Test
public void testSimpleService() throws Exception {
MockitoAnnotations.initMocks(this);
SimpleService mockedSimple = mock(SimpleService.class);
when(mockedSimple.doSimpleService(any(SimpleServiceRequest.class))).thenReturn(new SimpleServiceResponse("MockMock"));
SimpleController mockedController=PowerMockito.spy(controller);
SimpleServiceRequest simpleServiceRequest= new SimpleServiceRequest("ciao");
String requestAsStr=objectMapper.writeValueAsString(simpleServiceRequest);
MockMvc mMockMvc=MockMvcBuilders.standaloneSetup(mockedController).build();
MvcResult result= mMockMvc.perform(post("/test/simpleservice").content(requestAsStr).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
SimpleServiceResponse simpleServiceResponse=objectMapper.readValue(content,SimpleServiceResponse.class);
Assert.assertEquals("MockMockMockedSessionManager",simpleServiceResponse.getValue());
}
}
When running the test case I got a NullPointerEception on httpRequest.getHeader("Header");
My guess is that using the #InjectMocks annotation and then using
SimpleController mockedController=PowerMockito.spy(controller);
is the cause of the NullPointerException. I don't know how to preserve the #Autowire annotation processing on the controller Object. I already found a workaround, but it requires to write some redundant code.
Is there a way to make the #autowired annotation work?
Thanks a lot.
P.S.
the SimpleServiceXXX classes are like this one:
public class SimpleServiceResponse {
private String value;
public SimpleServiceResponse() {
}
public SimpleServiceResponse(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

How to retrieve a list data from properties file?

I would like to check if the user entered the country is in the list of properties.
public class CountryValidator implements ConstraintValidator<CountryValid,String> {
#Value("#{countryOptions}")
Map<String, String> countryOptions;
#Override
public boolean isValid(String girilenDeger, ConstraintValidatorContext arg1) {
// TODO Auto-generated method stub
return countryOptions.containsKey(girilenDeger);
}
#Override
public void initialize(CountryValid constraintAnnotation) {
// TODO Auto-generated method stub
ConstraintValidator.super.initialize(constraintAnnotation);
}
}
However, I have successfully used this list before in the controller class. I get NullPointerException error when I use it again in my validation class.
#Controller#RequestMapping("/customerForm")
public class CustomerController {
#Value("#{countryOptions}")
Map<String, String> countryOptions;
#RequestMapping("/mainMenu")
public String returnForm(Model model) {
model.addAttribute("theCountryOptions", countryOptions);
Customer customer1 = new Customer();
model.addAttribute("customer1", customer1);
return "customer-view/main-menu";
}
#RequestMapping("/resultPage")
public String returnResult(#Valid #ModelAttribute("customer1") Customer customer, BindingResult result,
Model model) {
model.addAttribute("theCountryOptions", countryOptions);
if (result.hasErrors())
return "customer-view/main-menu";
else {
AddDatabase<Customer> database = new AddDatabase<Customer>();
database.setObj(customer);
database.addData();
System.out.println("Ekleme islemi tamamlandı.");
return "customer-view/result-page";
}
}
}
Or can I retrieve theCountryOptions attribute from the model?

Why I can't Receive String and It's null

#RequestMapping(value = "/save",method = RequestMethod.POST)
#ResponseStatus(value= HttpStatus.OK)
public void save(String str) throws IOException {
System.out.println(str);
}
all I got is null:
You need to tell Spring where to get str from.
If you're sending the JSON
{ "str": "sasfasfafa" }
You'll need a class that deserialises from this and annotate the method parameter with #RequestBody.
public class StrEntity {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
public class MyController {
#RequestMapping(value = "/save",method = RequestMethod.POST)
#ResponseStatus(value= HttpStatus.OK)
public void save(#RequestBody StrEntity entity) throws IOException {
System.out.println(entity.getStr());
}
}
If you just want to send a string as the request body (i.e. sasfasfafa) instead of the JSON document you can do this:
public class MyController {
#RequestMapping(value = "/save",method = RequestMethod.POST)
#ResponseStatus(value= HttpStatus.OK)
public void save(#RequestBody String str) throws IOException {
System.out.println(str);
}
}
There is no way to send the JSON { "str": "sasfasfafa" } as request body and only have a String as a method parameter in the controller.
Use #RequestParam annotation to get the parameter.
#RequestMapping(value = "/save",method = RequestMethod.POST)
#ResponseStatus(value= HttpStatus.OK)
public void save(#RequestParam(name="str") String str) throws IOException {
System.out.println(str);
}

The method addObject(String, Object) from the type ModelMap is deprecated?

How can I achieve the following in spring mvc 3.2.2?
I get the following error:
The method addObject(String, Object) from the type ModelMap is deprecated?
model.addObject("obj", obj); // obj is pojo
Then want to access it within .jsp/jstl view
${obj.id}
Controller:
public class obj {
private String Id;
public void setId(String value) {Id = value;}
public String getId() {
return Id;
}
}
View
${obj.Id} <-- DOES NOT work
${obj.getId()} <-- works!
replace model.addObject() with model.addAttribute("obj", obj)

How to test POST spring mvc

My problem is to how to call this. I could do
MyObject o = new MyObject();
myController.save(o, "value");
but this is not what I would like to do. I would like the MyObject to be in the request post body? How can this be done?
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#Valid MyObject o, #PathVariable String value{
objectService.save(o);
}
Just to be clear I am talking about unit testing.
Edit:
#RequestMapping(value = "/", method = RequestMethod.POST)
public View postUser(ModelMap data, #Valid Profile profile, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return dummyDataView;
}
data.put(DummyDataView.DATA_TO_SEND, "users/user-1.json");
profileService.save(profile);
return dummyDataView;
}
See sample code below that demonstrates unit testing a controller using junit and spring-test.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
#Transactional
#ContextConfiguration(locations = {
"classpath:rest.xml"
})
public class ControllerTest{
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Before
public void setUp() throws Exception
{
this.request = new MockHttpServletRequest();
request.setContentType("application/json");
this.response = new MockHttpServletResponse();
}
#Test
public void testPost(){
request.setMethod("POST");
request.setRequestURI("/save/test"); //replace test with any value
final ModelAndView mav;
Object handler;
try{
MyObject o = new MyObject();
//set values
//Assuming the controller consumes json
ObjectMapper mapper = new ObjectMapper();
//set o converted as JSON to the request body
//request.setContent(mapper.writeValueAsString(o).getBytes());
request.setAttribute("attribute_name", o); //in case you are trying to set a model attribute.
handler = handlerMapping.getHandler(request).getHandler();
mav = handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
//Assert other conditions.
}
catch (Exception e)
{
}
}
}
You need to use RequestBody:
#Requestmapping(value="/save/{value}", method=RequestMethod.POST)
public void post(#RequestBody MyObject o, #PathVariable String value{
objectService.save(o);
}
general info about request body documentation : http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-ann-requestbody

Resources