Why doesn't Spring MVC throw an error when you POST to a controller action that accepts HTTP GET? - spring

I just noticed a weird problem as I've been testing my application. I was accidentally POSTing to a method that accepts HTTP GET (It was a typo - I'm a little tired), but the weird thing is that Spring was executing a GET action anyway - it wasn't throwing an error.
Here is the mapping for my GET action that I was POSTing to instead:
#RequestMapping(value = "/partialMapping/{partialMappingId}/edit", method = RequestMethod.GET)
public ModelAndView edit(#PathVariable long partialMappingId) {
return new ModelAndView(view("edit"), "partialMapping",
partialMappingService.findPartialMapping(partialMappingId));
}
What I would have expected was for Spring to say, "There is no action called /partialMapping/{partialMappingId}/edit for HTTP POST".
Instead... if you use the HandlerAdapter and pass it "POST" and "/partialMapping/1/edit", it runs my index action instead ("/partialMapping"). It doesn't throw an error. Why?
Is this a bug in spring, or is this desired behaviour? It's not a big deal when it comes to production code, but it surely makes debugging problems harder.
Here is the code I am using to execute a controller action in my tests:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) {
try {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
I found this code per another answer to a question on this site.

I believe your test code mimics the dispatch step that tries to find a matching Controller method signature after the URL and HTTP method have resolved. In other words, you are not testing your controller at the right level if you want to test the HTTP message bindings. For that kind of testing you would probably want to deploy to a server (perhaps embedded Jetty inside your test) and use RestTemplate to call it. That's what I do anyway.

If you annotate with Spring MVC annotations as below
#RequestMapping(method = RequestMethod.GET it should work.

Related

ajax Spring liferay

I'm using an architetture that has: AJAX, Liferay 6.2, Spring 4.3.18.RELEASE;
From AJAX i make a call to the backend, that pass throu liferay and reach my controller;
Now, I want to generete an exception in the controller in order to reach the the failure of the ajax call; I've googled a lot but I wasn't able to find the solution.
my controller:
#Controller("movemementCon")
#RequestMapping("VIEW")
public class movemementCon {
#ResourceMapping("getDis")
#ResponseBody
public ResponseEntity<BenException> getImp(ResourceRequest request, ResourceResponse response, Model model) throws BenException{
return new ResponseEntity<BenException>(HttpStatus.NOT_ACCEPTABLE);
}
But when i reach the code in javascript (the AJAX call) it ignored definitively that I've throw an exception;
The desidered behaviur is that i force to go to the error statment in the AJAX call.
In web browser you can't catch java errors on client side, because client doesn't know anything about backend. You have to catch that error in spring controller method and then return ResponseEntity with some status, response body, custom response headers, so you can notify client that something went wrong and handle it correctly there.
#Controller("movemementCon")
#RequestMapping("VIEW")
public class movemementCon {
#ResourceMapping("getDis")
#ResponseBody
public ResponseEntity<SomeClass> getImp(ResourceRequest request, ResourceResponse response, Model model) {
try{
deleteFromDatabase(model);
} catch(BenException e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}
Then in your JS read the status and execute right action.

Can I get Spring Validation errors in prehandle

I currently have something similar to this in all of my endpoints in my spring app.
if(bindingResult.hasErrors()){
return new ResponseEntity<>(BAD_REQUEST);
}
I would like to move this to a http interceptor so that I only need it in one place. However, I cannot figure out how to get all of the errors from the binding result in preHandle.
How would I get validation errors in preHandle, or some other time before it starts the actual route?
One way to achieve what I think you're looking for is to not include BindingResult as a method parameter. Given no BindingResult is included as a method argument Spring will throw a BindException exception. You can define an ExceptionHandler, generally I've placed these within a #ControllerAdvice, to handle the exception as needed. Below is some sample code
Controller
#PostMapping
public SomeReturnObject someMethod(#Valid SomeCommand command) {
// logic - no longer contains checks for binding result errors
}
As part of ControllerAdvice
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationControllerAdvice {
....
#ExceptionHandler(BindException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
protected SomeResponse handleBindException(BindException ex) {
// handle exception
}
}

Spring MVC Controller method mapping using form body

I'm building a small application to serve as a client for some third party library here at work. The API states that a Webhookis needed to respond some asynchronous events, but all their methods have the very same signature, apart from a changing _method field between the calls. For example, I have a _method = ping, media, etc.
I'd like to have separate methods on my controller to respond for each one of these methods. If the app allowed me to specify different URLs for each method it would be easy to use Spring MVC's #RequestMapping for each one of them. But I have to specify a single endpoint to receive all calls.
Is there a way (for example using Spring's HttpMessageConverter or something like that) to map different controller methods based on what the Request Body is? I've already tried with #RequestBody, #RequestParam but didn't seem to find anything.
I really, really didn't want to use a bunch of case, switch methods on a front controller to dispatch actions based on my _method field that comes with my POST data, so I happen to believe someone had this problem before and solved it intelligently.
Thanks a lot!
Edit 1: Providing source code
#Controller
#RequestMapping("/webhooks")
public class WebhookController {
#RequestMapping(method = RequestMethod.POST, params = {"_method=ping"})
#ResponseBody
public String ping(){
return "pong";
}
#RequestMapping(method = RequestMethod.POST, params = {"_method=media"})
#ResponseBody
public String media(){
return "media";
}
}
This is the answer:
{
"timestamp": 1440875190389,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.UnsatisfiedServletRequestParameterException",
"message": "Parameter conditions \"_method=ping\" not met for actual request parameters: ",
"path": "/webhooks"
}
Right, I got it working. The answer is a bit tricky so I wanted to register it here should anyone have such problem.
#Neil McGuigan pointed me on the right direction on his comment but I didn't pay attention at first. The main culprit here is a very, very, very bad API design on our remote application's side.
_method is a field used to specify non-standard HTTP verbs such as PUT, PATCH, DELETE, TRACE and so on. This field is filtered by HiddenHttpMethodFilter and the HttpServletRequest is wrapped with this 'new' method. You can see at the file's source how it works.
As I wanted this _method field to get thru the filter without modifying the whole request (and causing the errors because there's no such verb as pingor message on `RequestMethod) I firstly had to deactivate the filter. This could be done by two ways:
I could stop Spring Boot from automagically configuring Spring MVC, skipping WebMvcAutoConfiguration from being loaded when the ApplicationContext was loaded. As you can imagine this is a BIG, BIG, BIIIIG NO because, well, things could happen.
I could use a FilterRegistrationBean to disable the bad filter. Pretty simple and straightforward, this was the method I chose to use:
#Bean
public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Last but not least, I decided to give HiddenHttpMethodFilter a little extension to somehow improve how the requests were getting thru. The Java EE Spec is pretty clear on the Servlet Spec Commandments where it states:
Thou should not alter your request on your side. You must respect the sender (something like that)
Though I agree with this, for the sake of my mental stability I decided to alter it anyway. To achieve this, we can use a simple HttpServletRequestWrapper, override the chosen methods and filter the original request with the wrapped part. I ended up doing something like this:
public class WhatoolsHiddenHttpMethodFilter extends OrderedHiddenHttpMethodFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(OrderedHiddenHttpMethodFilter.DEFAULT_METHOD_PARAM);
if("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
List<String> whatoolsMethods = Arrays.asList("ping", "message", "carbon", "media", "media_carbon", "ack");
if(whatoolsMethods.contains(paramValue)){
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, "POST", paramValue);
filterChain.doFilter(wrapper, response);
} else {
WhatoolsHiddenHttpMethodFilter.HttpMethodRequestWrapper wrapper = new WhatoolsHiddenHttpMethodFilter
.HttpMethodRequestWrapper(request, method, null);
filterChain.doFilter(wrapper, response);
}
} else {
filterChain.doFilter(request, response);
}
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
private final String whatoolsMethod;
public HttpMethodRequestWrapper(HttpServletRequest request, String method, String whatoolsMethod) {
super(request);
this.method = method;
this.whatoolsMethod = whatoolsMethod;
}
#Override
public String getMethod() {
return this.method;
}
#Override
public String getHeader(String name) {
if("x-whatools-method".equals(name)){
return this.whatoolsMethod;
}
return super.getHeader(name);
}
#Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
if(this.whatoolsMethod != null){
names.add("x-whatools-method");
}
return Collections.enumeration(names);
}
}
}
So, what this does is to wrap the request with a new x-whatools-method header when the header is in my whatoolsMethods list. With this, I can easily use #RequestMapping's headers property and map the requests to the correct controller methdods.
Back to the initial question, I'm almost sure (well, 99,95% should be completely sure but let's not risk it) the params property on #RequestMapping works only for request parameters on GET URIs, e.g http://foo.bar/?baz=42. It won't work filtering parameters sent on the request's body.
Thanks Neil for your guidance, even if small! I hope this helps someone.
You can use params in a request mapping:
#RequestMapping(value="/foo", params={"_method=ping"})
Assuming these are post parameters that is
params DOES work for POST, I promise you
Here's my controller:
#Controller
#RequestMapping("/test1")
public class ParamTestController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody String getA(){
return "A";
}
#RequestMapping(method = RequestMethod.POST, params = {"b"})
#ResponseBody String getB(){
return "B";
}
}
Here's my test:

Spring aspects forward and cancel previous request

Here is my question
I want to intercept request before any spring controller call, check and modify request URI. After that it has to call another SPRING controller method.
I used mvc:interceptors however I want to configure it with annotations thats why I need a solution for #Aspect. Everything is working but controller called twice, for the original request and for the new request. Using interceptors I return false and it cancels it, how do I do about Aspect classes? Thank you
Here is my code:
#Component
#Aspect
public class TestAspect {
#Before("execution(* mycontroller.*(..)) &&" + "args(request,response)")
public void interceptUrl(HttpServletRequest request, HttpServletResponse response) {
System.out.println("#Aspect is running!");
System.out.println(request.getRequestURI());
if (request.getAttribute("client") == null) {
request.setAttribute("client", "test");
request.getRequestDispatcher("/newpath/contact").forward(request, response);
}
}
}
You should consider #Around advice instead of #Before. In this case you can simply not execute original request chain.

Exception handler for REST controller in spring

I want to handle exceptions so the URL information is automatically shown to the client. Is there an easy way to do this?
<bean id="outboundExceptionAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<!-- what property to set here? -->
</bean>
You have two choices:
Spring Reference 15.9.1 HandlerExceptionResolver
Spring HandlerExceptionResolvers ease the pain of unexpected
exceptions that occur while your request is handled by a controller
that matched the request. HandlerExceptionResolvers somewhat resemble
the exception mappings you can define in the web application
descriptor web.xml. However, they provide a more flexible way to
handle exceptions. They provide information about which handler was
executing when the exception was thrown. Furthermore, a programmatic
way of handling exceptions gives you more options for responding
appropriately before the request is forwarded to another URL (the same
end result as when you use the servlet specific exception mappings).
The HandlerExceptionResolver has one method, containing everything you need:
HandlerExceptionResolver.resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex)
Or if you need different handlers for different controllers: Spring Reference Chapter 15.9.2 #ExceptionHandler
#ExceptionHandler(IOException.class)
public String handleIOException(IOException ex, HttpServletRequest request) {
return "every thing you asked for: " + request;
}
Short question short answer
I'm doing the following trick:
#ExceptionHandler(Exception.class)
public ModelAndView handleMyException(Exception exception) {
ModelAndView mv = new ModelAndView("redirect:errorMessage?error="+exception.getMessage());
return mv;
}
#RequestMapping(value="/errorMessage", method=RequestMethod.GET)
#Responsebody
public String handleMyExceptionOnRedirect(#RequestParamter("error") String error) {
return error;
}
Works flawless.

Resources