Spring MVC UI service form submission behind Spring Cloud Zuul - spring-mvc

The problem is when a form POST happens and then the controller does a redirect it seems that the redirect does not know it is behind Zuul.
Form:
#RequestMapping(value = "/create-something", method = RequestMethod.GET)
public String getForm(Model model, #CookieValue(value = "XSRF-TOKEN", defaultValue = "none", required=true) String token) {
model.addAttribute("title", "Create New Something");
model.addAttribute("_csrf", token);
return "views/create-something";
}
POST:
#RequestMapping(value = "/create-something", method = RequestMethod.POST)
public String postForm(Model model, #ModelAttribute Something something) {
SomethingClient.createSomething(something);
return "redirect:" + something.getName() + "/";
}
This setup will result in the redirect trying to hit the internal IP of the UI service not the public Zuul URL.

After trying different approaches I finally settled on a direct URL. Not sure if there is a better solution but this does work and resolves the above problem.
New POST:
#RequestMapping(value = "/create-something", method = RequestMethod.POST)
public String postForm(Model model, #ModelAttribute Something something) {
SomethingClient.createSomething(something);
return "redirect:http://domain.com/ui/" + something.getName() + "/";
}
Another thought would be an interceptor. That felt like added complexity times a large number of UI services.

Related

Submit on create user page doesn't go to createuser method in spring-mvc controller

I normally work on JAX-RS REST services, and I have to make some changes to a webapp using Spring MVC, which I'm only superficially familiar with (the MVC part, not Spring).
I have a view that lets the user create new users. When the form is submitted, instead of hitting the appropriate action in the Spring MVC controller, the page just "blanks out", as if it forwarded to a view that doesn't exist.
It's not clear to me what to look for here. Perhaps there's some logging I can enable that will show what request mapping decision Spring makes?
I'll try to show the relevant pieces of code here.
Inside the jsp page that has the form for the user/password/name fields, there's something like this:
Create User
I set a breakpoint in "validateUserCredentials()", which does some trivial validation of the entry fields:
function validateUserCredentials(){
var inputPassword=/[a-zA-Z0-9_-]{4,14}/ ;
var inputUsername=/[a-zA-Z0-9_-]{4,14}/ ;
var error ="false";
if (document.f.password.value.search(inputPassword)==-1)
{
error="true";
}else if (document.f.username.value.search(inputUsername)==-1){
error="true";
}else if (document.f.fullname.value.length==0){
error="true";
}else if (document.f.displayName.value.length==0){
error="true";
}
if(error =="true"){
document.f.action="/account/createuser?error=true";
document.f.submit();
}else{
document.f.action="/account/createuser";
document.f.submit();
}
}
Here is an excerpt of the controller:
#Controller
#SessionAttributes(value= {"userRoleMap", "userStatusMap"})
public class AccountController {
private static Logger logger = LogManager.getLogger(AccountController.class);
#Autowired
private UserAccountService accountService;
#RequestMapping(value = "/account/getCurrentUsers", method = RequestMethod.GET)
public ModelAndView getCurrentUsers(ModelMap model) {
logger.debug("In getCurrentUsers.");
List<UserInfo> userInfoList =accountService.getCurrentUserList();
Users users = new Users();
users.setUserInfoList(userInfoList);
HashMap<String,String> userRoleMap=populateAndGetUseridRoleMap(userInfoList);
HashMap<String,Boolean> userStatusMap=populateAndGetUseridStatusMap(userInfoList);
model.addAttribute("userRoleMap",userRoleMap);
model.addAttribute("userStatusMap",userStatusMap);
return new ModelAndView("userRole","users",users);
}
#RequestMapping(value = "/account/createuser", method = RequestMethod.GET)
public String getCreateUserPage() {
logger.debug("In getCreateUserPage.");
return "createUser";
}
#RequestMapping(value = "/account/createuser", method = RequestMethod.POST)
public String createUser(#RequestParam("username") String newUser,#RequestParam("password") String password,
#RequestParam("role") String role,
#RequestParam("fullname") String fullname,
#RequestParam("displayName") String displayName,
#RequestParam(value="error", required=false) boolean error,ModelMap model) {
logger.debug("In createUser. newUser[" + newUser + "] password[" + password + "] role[" + role +
"] fullname[" + fullname + "] displayname[" + displayName + "] error[" + error +
"] model[" + model + "]");
When I click the submit button, the page just "blanks out". The url was at "/account/createuser" before the submit, and it didn't change after the submit.
When I looked at the log, I only saw this:
controllers.AccountController (AccountController.java:73) - In getCurrentUsers.
controllers.AccountController (AccountController.java:104) - In getCreateUserPage.
It didn't appear to get into the "createUser" method.
Update:
I also noticed earlier in the log the following line:
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod Mapped "{[/account/createuser],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String ...AccountController.createUser(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,boolean,org.springframework.ui.ModelMap)
This was only due to the fact that some of the application links in the jsp pages were "raw" relative references, instead of using "

Can Spring MVC controller set the URL to be shown in browser?

We have a Spring MVC Controller method which servers the url pattern /hello/{userName} .
#RequestMapping("/hello/{userName}")
public ModelAndView helloWorld(#PathVariable("userName") String productId) {
String message = "HELLO"+userName;
return new ModelAndView("hellopage", "message", message);
}
Here when we request /hello/Tom ,hellopage.html will be servered with URL in the browser http://localhost:8080/myApp/hello/Tom
We would need the URL to be http://localhost:8080/myApp/Tom .Is there any way I can set the URL to be shown in the browser when returning from the controller.
Sure you can do that using redirect. Write two controllers:
#RequestMapping("/hello/{userName}")
public string helloWorld(#PathVariable("userName") String userName, Model model) {
String message = "HELLO" + userName;
model.addAttribute("message", message);
return "redirect:" + userName;
}
#RequestMapping("/{userName}")
public ModelAndView userHello((#ModelAttribute("message") String message)) {
return new ModelAndView("hellopage", "message", message);
}
I think you could also use tuckey url-rewrite for this: http://tuckey.org/urlrewrite/
This allows Apache mod_rewrite functionality in Tomcat through the use of a filter, and you wouldn't have to write two controllers for everything.

HTTP Status 405 - Request method 'POST' not supported - jQuery.post()

Update: See here for solution
Using Spring MVC 4.
Here's my JavaScript code that makes the POST request:
$("input.toggleCourse").change(function(e){
var d = {
classID: classID,
courseID: courseID
};
$.post(
"<c:url value="/class/addCourse" />",
JSON.stringify(d))
.done(function(data){
alert("ok");
});
});
(Tried with and without JSON.stringify, tried full $.ajax instead of $.post)
Here's my controller
#RequestMapping(value = "/class/addCourse", method = RequestMethod.POST)
public #ResponseBody String addCourse(#RequestBody final CourseInClass cic) {
StringBuilder sb = new StringBuilder();
try{
Class c = classServ.findOne(cic.ClassID);
c.Courses.add(courseServ.findOne(cic.CourseID));
sb.append("{success:true}");
} catch (Exception e){
sb.append("{error:\"").append(e.getMessage()).append("\"}");
}
return sb.toString();
}
I checked the network log that it sends the correct headers to the correct url. Post requests work for normal forms, but not for this ajax call.
Thanks.
How do you think (String classID, String courseID) will be detected by Spring. i.e. how will the json object be mapped to java object.
If you want to use auto binding you can use jackson-mapper-asl. Take a look at this page
If you don't want to use it you can use #PathVariable,
change method signatures to public #ResponseBody String addCourse(#PathVariable String classID, #PathVariable String courseID) {..}
and then hit http://localhost:8080/<appname>/class/addCourse/<classID>/<courseID>

Issue with mvc:annotation-driven and #PathVariable

Not sure what I am doing wrong here but when I DO NOT define the mvc:annotation-driven in my servlet, the value returned from the #PathVariable is not getting displayed in my jsp page and when I do define the annotation-driven, all other links e.g. home gets broken and I get the The specified HTTP method is not allowed for the requested resource (Request method GET not supported).
#Controller("HealthCheckController")
#RequestMapping("/healthCheckSummary")
public class HealthCheckController {
private Log log = LogFactory.getLog(GolSimpleMappingExceptionResolver.class);
private HealthCheckService healthCheckService = null;
private IEappDataAccessFacade dataAccessFacade;
#RequestMapping(value = "/{username}/{password}", method = RequestMethod.GET)
public String getEappStatus(#PathVariable String username, #PathVariable String password, Model model){
String dbConnectivityStatus = getDatabaseConnectivityStatus() ? "online" : "offline";
if (!username.equals("lola") || !password.equals("123")) {
// wrong credentials were provided
log.error("The login credentials in the header are not valid." + username + password);
throw new RuntimeException( "Unable to continue, the login credentials in the header are not valid." + username + password);
}
model.addAttribute("healthCheckSummary", dbConnectivityStatus);
return "healthCheckSummary";
}
public HealthCheckService getHealthCheckService()
{
return healthCheckService;
}
public boolean getDatabaseConnectivityStatus() {
String result = “OK”;
if (result != null) {
return true;
}
return false;
}
}
Oh and in the application context we have defined the
<tx:annotation-driven />
JSP page
<%# page language="java"%>
Welcome ${username} - ${password}
Eapp is currently ${healthCheckSummary}
Two things:
You never put your username and password #PathVariables in the model. Your jsp page has no way of knowing that they even existed as you lose any reference to them after the code leaves getEappStatus. Add the following to your handler method:
model.addAttribute("username", username);
model.addAttribute("password", password);
and see if that works.
You might want to add <mvc:annotation-driven/> just to avoid some surprises when you use some spring-mvc annotations. This isn't strictly necessary, but might save you some head scratching when some #RequestBody doesn't work.

How to redirect to another Server from SpringController with Request Parameters

Could any one suggest me that how to redirect to another server from spring MVC Controller?
I could redirect to another server by adding the below code in my controller
redirect:http://www.okay.com
but the problem is I would like to receive the request with parameters in another server.
For using GET ,parameters can not be hidden. You can redirect through POST method.
Go through example :
#RequestMapping(method = RequestMethod.POST)
public ModelAndView postRedirect(){
String redirectUrl = "http://www.okay.com";
RedirectView vf = new RedirectView(redirectUrl);
vf.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
ModelAndView md = new ModelAndView(vf);
md.addObject("param1", "hello"); //Add your params here
return md;
}

Resources