I have the following Spring controller:
#Controller
public class TypoWorkflowController {
#RequestMapping(value = "/workflow/typo-workflow/moreInfo", method = RequestMethod.GET)
public String serveMoreInfo(#RequestParam(value = "taskId", required=false) String taskId, ModelMap modelMap) {
return "typo-workflow-more-info";
}
}
My tiles-def file contains:
<definition name="typo-workflow-more-info" template="/WEB_INF/jsp/workflow/typo-workflow/moreInfo.jsp"/>
My JSP is plain old HTML.
When I hit the url /workflow/typo-workflow/moreInfo, Tomcat throws a StackOverflowError.
When I step through in debug mode, I see that I'm hitting my controller first, as I would expect, but then I hit another controller, at the method:
#Controller
#Order(value = Ordered.LOWEST_PRECEDENCE)
public class ContentServingController {
/* ... */
#RequestMapping({"/*", "/**/*"})
public ModelAndView serveContent(HttpServletResponse response, ModelMap model) {
/* ... */
}
}
As I poked around, it seeeeeeemed like we were in there to respond to a request for /WEB_INF/jsp/workflow/typo-workflow/moreInfo.jsp, but this doesn't happen for other controllers that operate in the same way (returning a View name).
So, can anyone provide me with some pointers for debugging this. Why would I be hitting a controller for a JSP anyway? Isn't a JSP supposed to be a little servlet itself?
Your tiles def is pointing to the WEB_INF folder when it should be pointing to the WEB-INF folder (dash instead of underscore) so spring doesn't know where to look within the app and is just making a normal http request, which is getting caught by the wildcard match.
Related
#RequestMapping("/accounts")
public class controller {
#GetMapping("/get/{id}")
public final ResponseEntity<?> getHandler(){
}
#PostMapping(value = "/create")
public final ResponseEntity<?> createHandler(){
/*
trying to use some spring library methods to get the url string of
'/accounts/get/{id}' instead of manually hard coding it
*/
}
}
This is the mock code, now I am in createHandler, after finishing creating something, then I want to return a header including an URL string, but I don't want to manually concat this URL string ('/accounts/get/{id}') which is the end point of method getHandler(), so I am wondering if there is a method to use to achieve that? I know request.getRequestURI(), but that is only for the URI in the current context.
More explanation: if there is some library or framework with the implementation of route:
Routes.Accounts.get(1234)
which return the URL for the accounts get
/api/accounts/1234
The idea is, that you don't need to specify get or create (verbs are a big no-no in REST).
Imagine this:
#RequestMapping("/accounts")
public class controller {
#GetMapping("/{id}")
public final ResponseEntity<?> getHandler(#PathVariable("id") String id) {
//just to illustrate
return complicatedHandlerCalculation(id).asResponse();
}
#PostMapping
public final ResponseEntity<?> createHandler() {
//return a 204 Response, containing the URI from getHandler, with {id} resolved to the id from your database (or wherever).
}
}
This would be accessible like HTTP-GET: /api/accounts/1 and HTTP-POST: /api/accounts, the latter would return an URI for /api/accounts/2 (what can be gotten with HTTP-GET or updated/modified with HTTP-PUT)
To resolve this URI, you could use reflection and evaluate the annotations on the corresponding class/methods like Jersey does.
A Spring equivalent could be:
// Controller requestMapping
String controllerMapping = this.getClass().getAnnotation(RequestMapping.class).value()[0];
and
//Method requestMapping
String methodMapping = new Object(){}.getClass().getEnclosingMethod().getAnnotation(GetMapping.class).value()[0];
taken from How do i get the requestmapping value in the controller?
I'm writing a web app which only consists of Rest API endpoints. There is no jsp, nor any UI written in Java provided by the app. (I'm planning to write the front end in React in future)
Does it make sense to use ModelAndView in my controllers ? I want to do a redirect to another URL ? I see sample codes similar to the following code:
#Controller
#RequestMapping("/")
public class RedirectController {
#GetMapping("/redirectWithRedirectPrefix")
public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) {
model.addAttribute("attribute", "redirectWithRedirectPrefix");
return new ModelAndView("redirect:/redirectedUrl", model);
}
}
If your controller is always going to redirect, you can just return a String e.g.
return "redirect:/redirectedUrl";
Otherwise, you can return a response entity:
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", "/redirectedUrl");
return new ResponseEntity<Foo>(headers, HttpStatus.FOUND);
I don't think it makes sense to return a ModelAndView if there is no view.
I'd like to know if there is a way I can forward a request from one controller to another without actually changing the URL in the browser.
#RequestMapping(value= {"/myurl"})
public ModelAndView handleMyURL(){
if(somecondition == true)
//forward to another controller but keep the url in the browser as /myurl
}
examples that I found online were redirecting to another url which was causing other controllers to handle that. I don't want to change the URL.
Try to return a String instead of ModelAndView, and the String being the forward url.
#RequestMapping({"/myurl"})
public String handleMyURL(Model model) {
if(somecondition == true)
return "forward:/forwardURL";
}
Instead of forwarding, you may just call the controller method directly after getting a reference to it via autowiring. Controllers are normal spring beans:
#Controller
public class MainController {
#Autowired OtherController otherController;
#RequestMapping("/myurl")
public String handleMyURL(Model model) {
otherController.doStuff();
return ...;
}
}
#Controller
public class OtherController {
#RequestMapping("/doStuff")
public String doStuff(Model model) {
...
}
}
As far as I know "forward" of a request will be done internally by the servlet, so there will not be a second request and hence the URL should remain the same. Try using the following code.
#RequestMapping(value= {"/myurl"})
public ModelAndView handleMyURL(){
if(somecondition == true){
return new ModelAndView("forward:/targetURL");
}
}
I feel like this is a common problem but nothing I've researched has worked yet...
In my web.xml I have a mapping for all REST calls -
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
This works well if the URL is -
GET /rest/people
but fails if it is
GET /rest/people/1
I get a 400 Bad Request error saying The request sent by the client was syntactically incorrect (). I'm not sure it even made it to the Spring servlet to get routed...
How can I wildcard anything that starts with /rest so that it can be handled appropriately?
In other words, I'd like for all of the following to be valid -
GET /rest/people
GET /rest/people/1
GET /rest/people/1/phones
GET /rest/people/1/phones/23
Edit - Controller code as requested
#Controller
#RequestMapping("/people")
public class PeopleController {
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody String getPeople() {
return GsonFactory.getInstance().toJson(LookupDao.getInstance().getPeople());
}
#RequestMapping(value="{id}", method=RequestMethod.GET)
public #ResponseBody String getPerson(#PathVariable String id) {
return GsonFactory.getInstance().toJson(LookupDao.getInstance().getPerson(id));
}
}
Answer
#matsev It didn't seem to matter if I had the / there or not.
While I was transposing the variable names for public view I changed a couple things to make it work.
Original
#RequestMapping(value="{id}", method=RequestMethod.GET)
public #ResponseBody String getPerson(#PathVariable String userId) {
return GsonFactory.getInstance().toJson(LookupDao.getInstance().getPerson(userId));
}
What I posted
#RequestMapping(value="{id}", method=RequestMethod.GET)
public #ResponseBody String getPerson(#PathVariable String id) {
return GsonFactory.getInstance().toJson(LookupDao.getInstance().getPerson(id));
}
The variable name mismatch did me in... I leave this here as a warning to all... match your variable names!
Try add a /before the {id}:
#RequestMapping(value="/{id}", method=RequestMethod.GET)
Without it, the id will be appended directly to the people url, e.g /rest/people1 as opposed to /rest/people/1.
I have implemented a REST application with Spring at Java. An example of GET and DELETE requests are as follows:
#RequestMapping(method = RequestMethod.GET)
public
#ResponseBody
List<Configuration> getAllConfigurationsInJSON() {
return new ArrayList<Configuration>(configurationMap.values());
}
#RequestMapping(value = "{systemId}", method = RequestMethod.DELETE)
public void deleteConfiguration(HttpServletResponse response, #PathVariable long systemId) throws IOException {
if (configurationMap.containsKey(systemId)) {
configurationMap.remove(systemId);
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
I am searching about Grails and want to rewrite my controller with Grails. I read some articles and it shows that there is no need to write that annotations at Grails. I will just define my clousers and it will render my response to JSON object as like my Spring applicaiton. How can I implement them with closures? (I use IntelliJ IDEA 10.3)
There is nothing in this code that can make use of closures.
In grails it may look the same, or you can put the url mappings in UrlMappings.groovy