Add value into request object before validation - spring-mvc

I'm using #Restcontroller and #Vaid annotation.
#RequestMapping(path = "/1050"
method = RequestMethod.POST,
headers = {"Content-Type=application/json"},
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
public UserListResp getUserList(#RequestBody #Valid UserListReq request, BindingResult bindingResult,
Principal principal){
UserListResp response = new UserListResp();
if (bindingResult.hasErrors()){
response.setResultCode(102); // Validation error
response.setErrMsg("Wrong " + bindingResult.getFieldError().getDefaultMessage() + " value.");
} else {
return userService.getUserList(request) ;
}
return response;
}
Incoming request mapped to object which validated.
public class UserListReq {
private String userName;
....
}
I'm not getting this value (userName) from incoming json request, I've got from oAuth service by token.
Is it possible to send userName to validation constraint from #ControllerAdvice ?
#InitBinder
public void dataBinding(WebDataBinder binder, HttpServletRequest req) {
// userName
req.getUserPrincipal().getName();
}
Thanks.

I've found decision
#ControllerAdvice
public class GlobalControllerAdvice {
#InitBinder
public void dataBinding(WebDataBinder binder, HttpServletRequest request) {
binder.bind(new MutablePropertyValues(Collections.singletonMap("userName",request.getUserPrincipal().getName())));
}
}

Related

How to return HTTP response from HandlerMethodArgumentResolver

I have some controller's method:
#RequestMapping("/")
#AuthorizedRNUser
public Object index(UserStateVO userStateVO) {
return userStateVO;
}
Also I have HandlerMethodArgumentResolver for UserStateVO parameter
public class UserStateArgumentHandlerResovler implements HandlerMethodArgumentResolver{
#Autowired
RNService service;
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getMethod().isAnnotationPresent(AuthorizedRNUser.class) && methodParameter.getParameterType() == UserStateVO.class;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
UserStateVO userState = service.getUserState();
if (isNull(userState))
// here i need to return 403 HTTP response
throw new RuntimeException("User is not allowed");
return userState;
}
}
And if the UserStateVO is null I need to return 403 HTTP response, but I do not know is it possible? How best to check UserStateVO and pass it into a controller or return HTTP response?
Use the same method as handling exceptions in MVC exception-handling-in-spring-mvc
Add your custom exception, e.g
public class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
super(message);
}
}
And either annotate it with #ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "User is not allowed") or add #ControllerAdvice class with method like
#ExceptionHandler(value = { BadRequestException.class })
#ResponseStatus(value = HttpStatus.FORBIDDEN)
#ResponseBody
public Map<String, String> handleBadRequestException(BadRequestException e) {
Map<String, String> retMessages = new HashMap<>();
retMessages.put("message", e.getMessage());
return retMessages;
}
what remains is just to throw it
if (isNull(userState))
// here i need to return 403 HTTP response
throw new BadRequestException("User is not allowed");

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

get param value to generate a chart

user enter two date
it need to click on a button...
if the date are valid, the same jsp page is called and some value are setted in the request... in this jsp, if setSeachDone is true, a chart is generate...
another servled controller is called for the image... but the value already setted in the request are empty
#Controller
#RequestMapping("/statError")
public class StatisticError {
#Autowired
private IUserService userService;
#InitBinder("statisticForm")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new StatisticErrorFormValidator());
}
#RequestMapping(method = RequestMethod.GET)
public String statistic(Model model) {
StatisticErrorForm statisticForm = new StatisticErrorForm();
model.addAttribute("statisticForm", statisticForm);
return "statisticError";
}
#RequestMapping(method = RequestMethod.POST)
public String statistiqueResult(#Valid #ModelAttribute StatisticErrorForm statisticForm, BindingResult result, ModelMap model, HttpServletRequest request,
HttpServletResponse response) {
if (!result.hasFieldErrors() && !result.hasErrors()) {
request.setAttribute("startDate", statisticForm.getStartDate());
request.setAttribute("endDate", statisticForm.getEndDate());
statisticForm.setSearchDone(true);
}
model.addAttribute(statisticForm);
return "statisticError";
}
}
the servlet controller
#Controller
#RequestMapping("/statError.jpg")
public class ImageErrorController {
#Autowired
private IUserService userService;
#RequestMapping(method = RequestMethod.GET)
public void generateChart(HttpServletRequest request,
HttpServletResponse response) {
if (request.getAttribute("startDate") != null && request.getAttribute("endDate") != null) {
response.setContentType("image/jpg");
AxisChart axisChart = userService.generateChart();
ServletEncoderHelper.encodeJPEG(axisChart, 1.0f, response);
}
}
is there a way to send the value entered by the user to the imageErrorController?
add the model to generateChart?
You need to pass them as parameters of image URL in your view, like this:
<img src = "<c:url value = "/statError.jpg">
<c:param name = "startDate" value = "${startDate}" />
<c:param name = "endDate" value = "${endDate}" />
</c:url>" />

reCaptcha issue with Spring MVC

I've been trying to integrate reCaptcha with my application built on Spring framework, but I am getting this error:
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'recaptcha_challenge_field' is not present
Could someone help me understand that why am I getting this error. I've got both recaptcha_challenge_field and recaptcha_response_field parameters bound to the User domain object.
Could anybody help me understand what am I missing?
Thanks
Here is the code of the controller I am using, all I am trying to do is register a user with reCaptcha functionality but what I am getting is a http status 400 with the error org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'recaptcha_challenge_field' is not present:
UserManagementController.java
#Controller
public class UserManagementController {
private static final String RECAPTCHA_HTML = "reCaptchaHtml";
#Autowired
private UserService userService;
#Autowired
private ReCaptcha reCaptcha;
#RequestMapping(method=RequestMethod.GET, value="/addNewUser.do")
public ModelAndView addNewUser() {
User user = new User();
String html = reCaptcha.createRecaptchaHtml(null, null);
ModelMap modelMap = new ModelMap();
modelMap.put("user", user);
modelMap.put(RECAPTCHA_HTML, html);
return new ModelAndView("/addNewUser", modelMap);
}
#RequestMapping(method=RequestMethod.POST, value="/addNewUser.do")
public String addNewUser(#Valid User user, BindingResult result,
#RequestParam("recaptcha_challenge_field") String challenge,
#RequestParam("recaptcha_response_field") String response,
HttpServletRequest request,
Model model) {
verifyBinding(result);
String remoteAddr = request.getRemoteAddr();
ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddr, challenge, response);
if (!reCaptchaResponse.isValid()) {
result.rejectValue("captcha", "errors.badCaptcha");
}
model.addAttribute("user", user);
if (result.hasErrors()) {
result.reject("form.problems");
return "addNewUser";
}
return "redirect:showContent.do";
}
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields(new String[] {
"firstName", "lastName", "email",
"username", "password", "recaptcha_challenge_field", "recaptcha_response_field"
});
}
private void verifyBinding(BindingResult result) {
String[] suppressedFields = result.getSuppressedFields();
if (suppressedFields.length > 0) {
throw new RuntimeException("You've attempted to bind fields that haven't been allowed in initBinder(): "
+ StringUtils.join(suppressedFields, ", "));
}
}
}
Here is the addNewUser.jsp element on the form page for the above controller:
<tr>
<td>Please prove you're a person</td>
<td>${reCaptchaHtml}</td>
<td><form:errors path="captcha" cssStyle="color:red"></form:errors></td>
</tr>
Could you help me understand what am I missing here?
Thanks for reply.
What is the implementation of:
String html = reCaptcha.createRecaptchaHtml(null, null); ?
The reCaptcha html must have the name attribute as "recaptcha_challenge_field"
...
<textarea name="recaptcha_challenge_field" ... />
<input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
...
Captcha is dynamic loaded script on the page. It is better to read captcha parameters from request object as shown in below example:
#RequestMapping(value="/submitCaptcha.web",method = RequestMethod.POST)
public String submitCaptcha(#ModelAttribute("recaptchaBean") RecaptchaBean recaptchaBean,BindingResult result, ModelMap model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
String captchaChallenge = request.getParameter("recaptcha_challenge_field");
String captchaText = request.getParameter("recaptcha_response_field"); }

Spring MVC POJO Bind Explanation

I am trying to understand how the binding of objects works in spring mvc. I have a Controller set up as follows, and want to have the freemarker template bind to the accessRequestBean. In the template I have '<#spring.bind "command.accessRequestBean" />' but that causes errors... How do I bind a form to a POJO?
#Controller
#PreAuthorize("isAuthenticated()")
#RequestMapping("/access")
public class RemoteVendorAccessController {
private Logger logger = Logger.getLogger(this.getClass().getName());
#Autowired
private AdDao adDao;
#Autowired
private CadaDao cadaDao;
#Autowired
private UserAccessCache userAccessCache;
private AccessRequestBean accessRequestBean;
#RequestMapping(method = RequestMethod.GET)
public String requestAccess(ModelMap map){
String username = SecurityContextHolder.getContext().getAuthentication().getName();
map.addAttribute("title", "Remote Vendor Access Request Form");
try {
AdUser user = adDao.getUserFromNt(username);
map.addAttribute("user", user);
} catch (UserDoesNotExistException e) {
String error = "Could not get user information from AD";
map.addAttribute("error", error);
logger.error(error + "[" + username + "]", e);
}
// Get users manager
AdUser manager = null;
try {
manager = adDao.getManagerFromNt(username);
map.addAttribute("manager", manager);
} catch (Exception e) {
String error = "Could not get manager information from AD";
map.addAttribute("error", error);
logger.error(error + "[" + username + "]", e);
}
return("access");
}
#RequestMapping(method = RequestMethod.POST)
public String processRequest(ModelMap map){
// Want to validate POJO bean here
return(null);
}
public AccessRequestBean getAccessRequestBean() {
return accessRequestBean;
}
public void setAccessRequestBean(AccessRequestBean accessRequestBean) {
this.accessRequestBean = accessRequestBean;
}
}
According to the Spring Documentation, the controller gets a reference to the object holding the data entered in the form by using the #ModelAttribute annotation on a method parameter. The parameter type would be your POJO class which corresponds to the object used to construct the form on the edit template. i.e.
#RequestMapping(method = RequestMethod.POST)
public String processRequest(
#ModelAttribute POJO pojo,
BindingResult result,
ModelMap map){
new POJOValidator().validate(pojo, result);
if (result.hasErrors()) {
return "pojoForm";
}
.
.
.
return(null);
}

Resources