405 Method not allowed in Spring Boot and Ajax - java

Hi I am having request mapping like this
#RequestMapping(value = "/pendodnp/{visitorId}/{content}")
public ResponseEntity<Object> setPendoDNP(#PathVariable String visitorId, #PathVariable String content, final HttpSession session, final HttpServletRequest request) throws PendoException {
LOGGER.info("### pendodnp");
_splitService.updateDNP(visitorId, content);
return ResponseEntity.ok().body(content);
}
but when I hit this URL from an angular function, it gives "405 method not allowed" error.
$http.get("pendodnp/"+visitorId+"/"+dnpValue, JSON.stringify(dnpValue))
.success(function(response) {
Here is screenshot of how the request is going, can someone tell me what i am missing here?

You haven't specified which HTTP method (GET, POST, PUT, DELETE, etc) the mapping should handle. To do that either use #GetMapping or change your #RequestMapping so it includes the method parameter.
#GetMapping("/pendodnp/{visitorId}/{content}")
Or
import static org.springframework.web.bind.annotation.RequestMethod.GET;
...
#RequestMapping(method = GET, path = "/pendodnp/{visitorId}/{content}")
Using #RequestMapping without a method parameter is usually only done at the class level, not individual methods. #GetMapping (or #PostMapping, #PutMapping, etc) are more common on controller methods.

Related

Why doesn't DispatcherServlet invoke my HandlerInterceptor?

I know that in JavaEE, filters can intercept any request to a servlet. But Interceptors in Spring MVC are not exactly the same. If you look at the diagram below, you will see that Interceptors come after Dispatcher Servlet.
Let me give you an example before I ask my question.
I have a controller, which has 2 methods in it that are mapped to two different requests. One accepts GET requests and other accepts POST requests. Now if I add an interceptor in my web application, that interceptor will sit before Controller. Meaning that before controller method is hit, first a request will hit my interceptor's preHandle method.
Now say that in my app, two controllers methods look like this:
#Controller
public class myController{
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test1(){
return "abc";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String test1(){
return "xyz";
}
And lets say I have a simple interceptor like this:
public class URLInterceptors extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("REQUESTED SERVLET PATH IS: " + request.getServletPath());
return true;
}
}
Now, if I make a GET request to /test, my interceptor is hit and it prints the servlet path, but when I make a GET request to /login, I know it will fail because my method that handles /login mapping accepts only POST requests, however before it throws '405 Request method 'GET' not supported' error, it should at least hit first my interceptor? It doesn't. I I don't want to change POST to GET. So the question is why?
Part of this is explained in
Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?
In summary, the DispatcherServlet attempts to find an appropriate handler for your request by using a HandlerMapping (see your graphic). These handlers are actually adapters that wrap the actual handler method (a #RequestMapping annotated method in this case) with the interceptors you've registered. If this handler is found, then the DispatcherServlet can proceed, invoke interceptors, and, if required, invoke your handler method.
In your case, because your #RequestMapping is restricted to POST requests and your request is a GET, the DispatcherServlet fails to find an appropriate handler and therefore returns an error before it's had a chance to invoke any interceptors.
Note that the javadoc states
A HandlerInterceptor gets called before the appropriate HandlerAdapter
triggers the execution of the handler itself.
but your DispatcherServlet never found a handler to begin with.
You might want to consider using a Servlet Filter instead.

Spring MVC: How to set body parameter in request browser and how to get this body parameters in controller in Spring MVC?

I followed may links and found I need to use #Requestbody annotation and I need to set Content-Type=application/x-www-form-urlencoded in header under #RequestMapping annotation. But I did not find any example like how can I set these body parameters in browser and get in controller
#RequestMapping(value = "/login", headers="application/x-www-form-urlencoded" , method = RequestMethod.GET)
public void login(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServiceException {
// I need username and password body parameters value in controller
}
You would need something like Postman client in the browser.
You can install it from here. After installation, you can refer to answer to this question to know how to use it.

Spring 3.x - how do I redirect from a mapping that returns data?

I have a method in my controller like this:
#RequestMapping(value="getData", method=RequestMethod.GET)
#ResponseBody
public List<MyDataObj> getData()
{
return myService.getData();
}
The data is returned as JSON or xsl, depending on the request.
If the person making the request is not authorized to access the data I need to redirect the user to a "not authorized" page, so something like this:
#RequestMapping(value="getData", method=RequestMethod.GET)
#ResponseBody
public List<MyDataObj> getData()
{
if (!isAuthorized())
{
// redirect to notAuthorized.jsp
}
return myService.getData();
}
All the examples I've seen using Spring require the method to return either a String or a ModelAndView. I thought about using HttpServletResponse.sendRedirect() but all my JSPs are under WEB-INF and can't be reached directly.
How can I deny access gracefully to the data request URL?
A more elegant solution may be to use a HandlerInterceptor which would check for authorization, blocking any requests which are not permitted to proceed. If the request then reaches your controller, you can assume it's OK.
The answer is below. But your particular case would rather to handle with other approach.
#RequestMapping(value = "/someUrl", method = RequestMethod.GET)
#ResponseBody
public Response someMethod(String someData, HttpServletResponse response){
if(someData=="redirectMe"){
response.sendRedirect(ppPageUrl);
}
...
}
Another approach is filter. You can move all security logic into filter and keep clean code in controllers.
Pretty simple:
Send a error status to the client
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
and handle the same with you ajax callback handler and redirect. :)

Spring - Redirect after POST (even with validation errors)

I'm trying to figure out how to "preserve" the BindingResult so it can be used in a subsequent GET via the Spring <form:errors> tag. The reason I want to do this is because of Google App Engine's SSL limitations. I have a form which is displayed via HTTP and the post is to an HTTPS URL. If I only forward rather than redirect then the user would see the https://whatever.appspot.com/my/form URL. I'm trying to avoid this. Any ideas how to approach this?
Below is what I'd like to do, but I only see validation errors when I use return "create".
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
#ModelAttribute("register") #Valid final Register register,
final BindingResult binding) {
if (binding.hasErrors()) {
return "redirect:/register/create";
}
return "redirect:/register/success";
}
Since Spring 3.1 you can use RedirectAttributes. Add the attributes that you want to have available before doing the redirect. Add both, the BindingResult and the object that you are using to validate, in this case Register.
For BindingResult you will use the name: "org.springframework.validation.BindingResult.[name of your ModelAttribute]".
For the object that you are using to validate you will use the name of ModelAttribute.
To use RedirectAttributes you have to add this in your config file. Among other things you are telling to Spring to use some newer classes:
<mvc:annotation-driven />
Now the errors will be displayed wherever you are redirecting
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(#ModelAttribute("register") #Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {
if (binding.hasErrors()) {
attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
attr.addFlashAttribute("register", register);
return "redirect:/register/create";
}
return "redirect:/register/success";
}
In addition to Oscar's nice answer, if you are following that RedirectAttributes approach, do not forget that you are actually passing the modelAttribute to the redirected page. This means if you create a new instance of that modelAttribute for the redirected page (in a controller), you will lose the validation errors. So, if your POST controller method is something like this:
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(#ModelAttribute("register") #Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {
if (binding.hasErrors()) {
attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
attr.addFlashAttribute("register", register);
return "redirect:/register/create";
}
return "redirect:/register/success";
}
Then you will probably need to do a modification in your register create page GET controller. From this:
#RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
// some stuff
model.addAttribute("register", new Register());
// some more stuff
}
to
#RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
// some stuff
if (!model.containsAttribute("register")) {
model.addAttribute("register", new Register());
}
// some more stuff
}
Source: http://gerrydevstory.com/2013/07/11/preserving-validation-error-messages-on-spring-mvc-form-post-redirect-get/
I would question why you need the redirect. Why not just submit to the same URL and have it respond differently to a POST? Nevertheless, if you really want to do this:
#RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
#ModelAttribute("register") #Valid final Register register,
final BindingResult binding,
HttpSession session) {
if (binding.hasErrors()) {
session.setAttribute("register",register);
session.setAttribute("binding",binding);
return "redirect:/register/create";
}
return "redirect:/register/success";
}
Then in your "create" method:
model.put("register",session.getAttribute("register"));
model.put("org.springframework.validation.BindingResult.register",session.getAttribute("register"));
The problem is you're redirecting to a new controller, rather than rendering the view and returning the processed form page. You need to do something along the lines of:
String FORM_VIEW = wherever_your_form_page_resides
...
if (binding.hasErrors())
return FORM_VIEW;
I would keep the paths outside of any methods due to code duplication of strings.
The only way to persist objects between requests (ie a redirect) is to store the object in a session attribute. So you would include "HttpServletRequest request" in method parameters for both methods (ie, get and post) and retrieve the object via request.getAttribute("binding"). That said, and having not tried it myself you may need to figure out how to re-bind the binding to the object in the new request.
Another "un-nicer" way is to just change the browser URL using javascript
I don't know the exact issue with Google App Engine but using the ForwardedHeaderFilter may help to preserve the original scheme that the client used. This filter was added in Spring Framework 4.3 but some Servlet containers provide similar filters and the filter is self-sufficient so you can also just grab the source if needed.
Perhaps this is a bit simplistic, but have you tried adding it to your Model? I.e., include the Model in your method's arguments, then add the BindingResult to it, which is then available in your view.
model.addAttribute("binding",binding);
I think you may have to use a forward rather than a redirect (in my head I can't remember if a redirect loses the session or not — I could be wrong about this as I don't have any documentation handy, i.e., if you're not getting the BindingResult after adding it to the Model, try using a forward instead to confirm this).

Get the variable in the path of a URI

In Spring MVC I have a controller that listens to all requests coming to /my/app/path/controller/*.
Let's say a request comes to /my/app/path/controller/blah/blah/blah/1/2/3.
How do I get the /blah/blah/blah/1/2/3 part, i.e. the part that matches the * in the handler mapping definition.
In other words, I am looking for something similar that pathInfo does for servlets but for controllers.
In Spring 3 you can use the # PathVariable annotation to grab parts of the URL.
Here's a quick example from http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/
#RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(#PathVariable("hotel") long hotelId, #PathVariable("booking") long bookingId, Model model) {
Hotel hotel = hotelService.getHotel(hotelId);
Booking booking = hotel.getBooking(bookingId);
model.addAttribute("booking", booking);
return "booking";
}
In Spring 2.5 you can override any method that takes an instance of HttpServletRequest as an argument.
org.springframework.web.servlet.mvc.AbstractController.handleRequest
In Spring 3 you can add a HttpServletRequest argument to your controller method and spring will automatically bind the request to it.
e.g.
#RequestMapping(method = RequestMethod.GET)
public ModelMap doSomething( HttpServletRequest request) { ... }
In either case, this object is the same request object you work with in a servlet, including the getPathInfo method.

Categories