From my understanding when Request Method is Get we should not have Request body, we use #RequestParam to read the url parameters. But spring test seems to be allowing it. Below is a sample code.
#Controller
public class SomeController {
#RequestMapping(value = "/abc", method = RequestMethod.GET)
public String greeting(#RequestBody Student student) {
return "Hello "+ student.name;
}
}
This is the test code and jsonString is the string representation of a Student object.
#Test
public void shouldReturnMessage() throws Exception {
this.mockMvc.perform(get("/abc").content(jsonString)).andDo(print()).andExpect(status().isOk());
}
My question here is, should Spring stop allowing #RequestBody with Get request? The above test runs fine and it's able to return me the student name. But when I try to call the same endpoint using curl it throws an error.
Reference : HTTP GET with request body
Related
I have a Model class and a controller. I am posting json type data in the body of post man. But each time i'm getting an unsupported media type 415 error.
Here is My Model class :
public class Model1 {
private String name;
private String password;
public Model1() {
System.out.println("In the Model");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String toString() {
return "Model1 [name=" + name + ", password=" + password + "]";
}
}
And My Controller is :
#Controller
#ResponseBody
public class EcomController {
#RequestMapping(value="/getLogin", method=RequestMethod.POST,
consumes=MediaType.APPLICATION_JSON_VALUE)
public String getLoginStatus(#RequestBody Model1 name){
return "successfully called Post"+name.toString();
}
}
I have used HttpServletRequest in the place of #RequestBody and it worked. But Why its not woking when I am using #RequestBody?
This is the postman snapshot.
Here is the image of request from postman
{
"name": "anshsh",
"password": "vbvfjh"
}
This is the Screen shot of headers used in the request
I found the mistake in my configuration file.
adding <mvc:annotation-driven /> solved the problem.
Explanation :
My program was running well but problem was on Mapping the String (Json) into java objects. So there was some issue in default Mapping classes. Although, jackson was present in class-path, It wasn't working.
<mvc:annotation-driven />
this tag would register the HandlerMapping and HandlerAdapter required to dispatch requests to your #Controllers. In addition, it also applies some defaults based on what is present in your classpath. Such as: Support for reading and writing JSON, if Jackson is on the classpath.
There are two areas to check for such issues.
Add Content-type as application/json in the request (which you
have already done)
Make sure you have jackson (core and data-bind)
libraries in the classpath
The point #2 is to make sure that the application is able to convert between JSON and Java types.
To narrow down the issue further, check the logs and try GET API and see if the Java object is converted to JSON string.
Refer this link for complete working code.
You don't need to put #ResponseBody there
also some example would be
#RestController
public class EcomController {
#PostMapping("/getLogin")
public String getLoginStatus(#RequestBody Model1 name){
return "successfully called Post"+name.toString();
}
}
If you keep getting error, I think the problem is the servlet didn't find your controller registered.. Can you give more information in detail like the log when you try to compile?
I am building APIs with spring rest and trying to validate the input parameters, like this:
#RequestMapping("/myUrl")
#RestController
#Validated
public class MyController {
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "getSomething", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
public myResponse getMarketResult(
#RequestParam #NotNull #NotBlank #NotEmpty String inputParam) {
//...my implementation
}
}
The validation does work in all these scenarios (null, blanks, empty).
MissingServletRequestParameterException is thrown for null, ConstraintViolationException is thrown for blanks/empty.
Controller advice:
#ControllerAdvice
public class ControllerValidationHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(NestedServletException.class)
public ResponseEntity<Object> processValidationError(NestedServletException ex, HttpStatus status, WebRequest request) {
// my implementation
}
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> processValidationError(ConstraintViolationException ex, HttpStatus status, WebRequest request) {
// my implementation
}
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// my implementation
}
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleExpception(HttpServletRequest req, Exception ex, WebRequest request) {
// my implementation
}
However I observe:
ConstraintViolationException is thrown as the cause of org.springframework.web.util.NestedServletException.
ControllerAdvice is unable to catch ConstraintViolationException and NestedServletException (But MissingServletRequestParameterException works), it's now returning a html (instead of json).
The 2 exceptions are not processed by 'handleException' of ResponseEntityExceptionHandler as well.
How do I solve 1 and 2? I'm using Spring version 4.3.1
Edit 1:
One more example, if I have a parameter like this:
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) #NotNull LocalDate localDate
Expcetion is thrown as mentioned in (1) when the request is empty (eg '/myURL/getSomething?localDate='
I am using spring mvc application. I want to redirect from controller or jsp to my second application that was developed in plain servlet and jsps.
How can I navigate flow from one apps servlet/jsps to another apps jsp.
I have used following lines in my controller to navigate:
First:
return new ModelAndView("redirect:/http://localhost:9090/MarathiInput/test.jsp");
Second:
response.sendRedirect("http://localhost:9090/MarathiInput/test.jsp");
Currently my controller is :
#RequestMapping(value = "/transferCertificate", method = RequestMethod.GET)
public ModelAndView get(ModelMap map ,HttpServletResponse response) {
response.sendRedirect("localhost:9090/MarathiInput/test.jsp");
}
and in my jsp i am calling :
Generate TC this link
You have small errors in both tries, but both can be used.
Assuming method controller is declared to return a ModelAndView you can use :
return new ModelAndView("redirect:http://localhost:9090/MarathiInput/test.jsp");
If it is declared to return a String :
return "redirect:http://localhost:9090/MarathiInput/test.jsp";
Alternatively provided the controller method has the response as parameter, you can do the redirect in controller, but as you have already processed the response, the method must return null :
response.sendRedirect("http://localhost:9090/MarathiInput/test.jsp");
return null;
So you could use :
#RequestMapping(value = "/transferCertificate", method = RequestMethod.GET)
public ModelAndView get(ModelMap map ,HttpServletResponse response) {
response.sendRedirect("http://localhost:9090/MarathiInput/test.jsp");
return null;
}
or simply :
#RequestMapping(value = "/transferCertificate", method = RequestMethod.GET)
public String get(ModelMap map ,HttpServletResponse response) {
return "redirect:http://localhost:9090/MarathiInput/test.jsp");
}
But make sure that the link includes the servlet context and servlet path :
Generate TC this link
I am adding rate-limiting to a restful webservice using Spring MVC 4.1.
I created a #RateLimited annotation that I can apply to controller methods. A Spring AOP aspect intercepts calls to these methods and throws an exception if there have been too many requests:
#Aspect
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class RateLimitingAspect {
#Autowired
private RateLimitService rateLimitService;
#Before("execution(* com.example..*.*(.., javax.servlet.ServletRequest+, ..)) " +
"&& #annotation(com.example.RateLimited)")
public void wait(JoinPoint jp) throws Throwable {
ServletRequest request =
Arrays
.stream(jp.getArgs())
.filter(Objects::nonNull)
.filter(arg -> ServletRequest.class.isAssignableFrom(arg.getClass()))
.map(ServletRequest.class::cast)
.findFirst()
.get();
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedAttempt(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
}
}
This all works perfectly, except when the #RateLimited controller method has parameters marked as #Valid, e.g.:
#RateLimited
#RequestMapping(method = RequestMethod.POST)
public HttpEntity<?> createAccount(
HttpServletRequest request,
#Valid #RequestBody CreateAccountRequestDto dto) {
...
}
The problem: if validation fails, the validator throws MethodArgumentNotValidException, which is handled by an #ExceptionHandler, which returns an error response to the client, never triggering my #Before and therefore bypassing the rate-limiting.
How can I intercept a web request like this in a way that takes precedence over parameter validation?
I've thought of using Spring Interceptors or plain servlet Filters, but they are mapped by simple url-patterns and I need to differentiate by GET/POST/PUT/etc.
I eventually gave up on trying to find an AOP solution and created a Spring Interceptor instead. The interceptor preHandles all requests and watches for requests whose handler is #RateLimited.
#Component
public class RateLimitingInterceptor extends HandlerInterceptorAdapter {
#Autowired
private final RateLimitService rateLimitService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HandlerMethod.class.isAssignableFrom(handler.getClass())) {
rateLimit(request, (HandlerMethod)handler);
}
return super.preHandle(request, response, handler);
}
private void rateLimit(HttpServletRequest request, HandlerMethod handlerMethod) throws TooManyRequestsException {
if (handlerMethod.getMethodAnnotation(RateLimited.class) != null) {
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedInvocation(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
} else {
rateLimitService.recordInvocation(ip);
}
}
}
}
Add the following controller advice in your application.
#ControllerAdvice
public class ApplicationControllerAdvice {
#InitBinder
#RateLimited
protected void activateBeanPropertyAccess(DataBinder dataBinder) {
dataBinder.initBeanPropertyAccess();
}
}
The #RateLimited should call the class RateLimitingAspect. So, after this all the constraints validator will be called.
See if it's feasible for you to implement similar logic for ##AfterThrowing advice as well which will have similar pointcut.
I have a requirement of achieving the method of controller in interceptor and the object returned by the method in interceptor.
why?
Because I want to declare the datatype which will return to client using annotation annotated on the method. for example :
#Controller
#Scope("prototype")
#RequestMapping("/hello/")
public class HelloWorld {
#ResponseType(DataType.JSON)
#RequestMapping(value="/{username}")
public UserInfo hellowUser(#PathVariable("username") String username) {
UserInfo userInfo = new UserInfo();
userInfo.setUsername(username);
return userInfo.
}
}
then interceptor:
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throw Exception {
Method method = getRequestedMethod();
Object result = getResultReturnedByTheMethod();
ResponseType responseType = method.getAnnotation(ResponseType.class);
DataType type = responseType.value();
swich(type) {
case DataType.JSON : writeJson(result);
case .......
...
}
}
So, in another words, how can I implement the "getRequestedMethod" and "getResultReturnedByTheMethod" correctly?
Have you tried the Jackson processor? http://jackson.codehaus.org/
It automatically converts JSON to and from the controller. And is supported by Spring MVC.