Spring MVC POJO Bind Explanation - spring-mvc

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

Related

Call a rest web api in asp.net and receive error

Hi I want to call a rest Web Api and I use asp.net MVC+Web Api.
I write a get Token Method like below :
public TokenViewModel GetToken()
{
//string Result = string.Empty;
TokenViewModel token = null;
string baseAddress = "http://$$$$$$$$$$/api/security/login";
using (HttpClient client = new HttpClient())
{
try
{
var url = new Uri(baseAddress);
MultipartFormDataContent form = new MultipartFormDataContent();
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("UserName", "###");
parameters.Add("Password", "$$$");
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(DictionaryItems, "model");
var response = client.PostAsync(url.ToString(), form, System.Threading.CancellationToken.None);
if (response.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
//Get body
var bodyRes = response.Result.Content.ReadAsStringAsync().Result;
token = JsonConvert.DeserializeObject<TokenViewModel>(bodyRes);
//Get Header
// var headers = response.Result.Headers.GetValues("appToken");
}
else
{
var a = response.Result.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
}
return token;
}
}
And also webController:
namespace WebAPI.Controllers
{
public class WebApiController : ApiController
{
private readonly GetToken_BLL _tokenService;
public WebApiController(GetToken_BLL tokenService)
{
_tokenService = tokenService;
}
public object Verfiybll { get; private set; }
public class stcAPIMessage
{
public string Message { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
[HttpPost]
[Route("api/Token")]
public IHttpActionResult Token()
{
stcAPIMessage message = new stcAPIMessage();
GetToken_BLL tokenbll = new GetToken_BLL();
var result = tokenbll.GetToken();
if (result == null)
{
message.Message = "error in recieveing token";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
else if (string.IsNullOrEmpty(result.Token))
{
message.Message = "Error";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
return Ok(result);
}
}
}
When I run the program it throw out error:
An error occurred when trying to create a controller of type 'Web ApiController'.
Make sure that the controller has a parameter less public constructor.
System. Invalid Operation Exception Type 'WebAPI.Controllers.
Web ApiController' does not have a default constructor
System.
The parameter less constructor error is common in ASP.NET web applications that use dependency injection.
I have noticed there is a constructor parameter being used:
GetToken_BLL _tokenService
Use a dependency injection resolver for the type GetToken_BLL so that the parameter _tokenService can be instantiated.

Add value into request object before validation

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

Spring MVC ExceptionHandler for restful and normal

I want to handle exception for both normal and rest/ajax requests. Here is my code,
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleCustomException(Exception ex) {
ModelAndView model = new ModelAndView("error");
model.addObject("errMsg", ex.getMessage());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
sw.toString();
model.addObject("errTrace", sw);
return model;
}
#ExceptionHandler(Exception.class)
#ResponseBody
public String handleAjaxException(Exception ex) {
JSONObject model = new JSONObject();
model.put("status", "error");
model.put("errMsg", ex.getMessage());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
sw.toString();
model.put("errTrace", sw);
return model.toString();
}
}
This will give me an error as I cant have #ExceptionHandler(Exception.class) twice. So what could be the solution?
see the configuration of #ControllerAdvice:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
So you can create two classes(error handlers) and specify annotations/basePackages/assignibaleTypes
For example for REST(ajax) use #RestController annotation for your controllers and you can handle errors like this:
#ControllerAdvice(annotations = RestController.class)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
#ResponseBody
public String handleAjaxException(Exception ex) {
...
}
}
for other cases it can be error handler with annotation
#ControllerAdvice(annotations = Controller.class)
This is global exception handler in spring mvc.this is called every time when exception found in your application.I think you to control only 404 exception with the help of web.xml.
#ControllerAdvice
public class GlobalExceptionController {
#ExceptionHandler(Throwable.class)
#ResponseBody
public ModelAndView handleAllException(Throwable ex,
HttpServletResponse response) {
ex.printStackTrace();
// Set Status
response.setStatus(500);
// Set View
ModelAndView model = new ModelAndView("500");
model.addObject("navlabel", "");
model.addObject("userActivity", new ArrayList<String>());
// Set exception Message
model.addObject("errMsg", ex.getMessage());
return model;
}
}
You can create inner static class #RestControllerAdvice. Noot needed to create separated #RestController for this.
#ControllerAdvice
public class BaseController {
private static final Logger logger =
LoggerFactory.getLogger(BaseController.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(Exception error, Model model) {
logger.error("Error was: " + error.getMessage(), error);
model.addAttribute("message", error.getMessage());
model.addAttribute("stackTrace", error.getStackTrace());
model.addAttribute("exception", error);
return "error"; //return view
}
#RestControllerAdvice
public static class RestBaseController {
private static final Logger logger = LoggerFactory.getLogger(RestBaseController.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(Exception error) {
logger.error("Error was: " + error.getMessage(), error);
return "error"; //return "error"
}
}
}
You can write two exception handler to handle both normal and rest/ajax requests exception. Here is sample code to illustrate the solution.
#ControllerAdvice(annotations = RestController.class)
#Order(1)
class RestExceptionHandler {
#ExceptionHandler(MyException.class)
#ResponseBody
ResponseEntity<ErrorResponse> exceptionHandler() {
....
}
}
#ControllerAdvice(annotations = Controller.class)
#Order(2)
class ExceptionHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleError500(HttpServletRequest request, HttpServletResponse response, Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("error", "500");
return mav;
}
}

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

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

Resources