I have defined a controller which serves local content like this:
#RequestMapping(value = PATH_CONTENT, method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<FileSystemResource> getContent(
#PathVariable(URI_PATH_PARAM_CONTAINER_UID) String containerUid,
#PathVariable(URI_PATH_PARAM_DOC_UID) String docUid) {
...
}
In the client side:
I am adding ResourceHttpMessageConverter to RestTemplate messageConverters.
When I finally make the following call:
ResponseEntity<FileSystemResource> response = getMyRestClient()
.getContent(url, null, FileSystemResource.class);
FileSystemResourcecontent = response.getBody();
This produces a ClassCastException saying it cant cast ByteArrayResource to FileSystemResource.
I can do some modification to my code to return a Resource instead of FileSystemResource and things will work. But I am really wondering why is this exception? I also don't want to take the path of writing to HttpServletResponse directly as I want to use Spring's HttpMessageConverter framework.
Related
I'm trying to write a simple HTTP REST service using Spring 4.
I'm having troubles sending data to a POST endpoint
#RequestMapping(value = "/onlyPost", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestParam("value1")
String param1, #RequestParam("value2") String param2) {
....
}
While trying to send data with Postman, I receive a 400 message (obviously the values are setted in the request's body)
"message": "Required String parameter 'value1' is not present",
What I have noticed is that the issue is somewhat related to the headers, because when I remove the postman's header (Content-Type: application/json) everything works fine.
I tried for more than one hour fixing this by myself with no results. Any hints?
#RequestParam is used to read a URL query parameter.
http://localhost:8080/springmvc/onlyPost?value1=foo&value2=bar
For instance, in the URL above, value1 and value2 are query parameters that you can read using that annotation.
But if you want to read a JSON request instead, you need to change the method to:
#RequestMapping(value = "/onlyPost", method = RequestMethod.POST,
consumes = "application/json")
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
....
}
where User is a POJO holding the two fields:
public class User {
private String value1;
private String value2;
// getters and setters...
}
HTTP 400 is returned when your request is badly formatted, i.e. missing required request parameters
#RequestParam is for URL Params, if you want to pass them like that, you call
<api_url>/onlyPost?value1=<value1>&value2=<value2>
but... if you want to create user you should rather use #RequestBody and put your user data there. Something like that:
#RequestMapping(value = "/users", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
[...]
}
if you are creating REST api you should use concrete endpoints, here is a pretty cool reading with some tips: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
long story short: I'm creating API that is supposed to be 100% REST.
I'm trying to overwrite default response for the following case:
I've got a method in my #RestController that has #RequestBody as an attribute
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody User user, HttpServletRequest request)
and the method is working just fine if I send a proper request. But there is a problem when I don't. When a request has empty body, I get a generic Tomcat error page for status 400 and I need it to send just a string or a JSON object instead.
So far I tried to add Exception Handlers in my RestControllerAdvice for all Spring exceptions from package org.springframework.web.binding, but it didn't work either.
I'm already aware that for some security-related errors one have to create handlers in configuration, but I don't know if this is the case.
Did anyone face similar issues? Is there something I'm missing?
The solution was to simply put required = false in RequestBody annotation. After that, I could easily add some logic to throw custom exception and handle it in ControllerAdvice.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody(required = false) User user, HttpServletRequest request){
logger.debug("addClient() requested from {}; registration of user ({})", getClientIp(request), user);
if(user == null){
throw new BadRequestException()
.setErrorCode(ErrorCode.USER_IS_NULL.toString())
.setErrorMessage("Wrong body or no body in reqest");
} (...)
Firstly I suggest you to use BindingResult as a parameter of the POST call and check if it returns an error or not.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public ResponseEntity<?> registerClient(#RequestBody User user, HttpServletRequest request, BindingResult brs)
if (!brs.hasErrors()) {
// add the new one
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
return new ResponseEntity<String>(brs.toString(), HttpStatus.BAD_REQUEST);
}
Secondly, the call can throw some of errors, a good practice is to carch them and return them itself or transform them to your own exception object. The advantage is it secures a call of all the update/modify methods (POST, PUT, PATCH)
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return new ResponseEntity<List<MethodArgumentNotValidException>>(e, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({HttpMessageNotReadableException.class})
#ResponseBody
public ResponseEntity<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
return new ResponseEntity<List<HttpMessageNotReadableException>>(e, HttpStatus.BAD_REQUEST);
}
Your control will never reach to your request method under normal circumstances.
If you want a looking good page you can make use of web.xml and configure it to produce your answer.
<error-page>
<error-code>404</error-code>
<location>/pages/resource-not-found.html</location>
</error-page>
Generally, if you want to go past this 400 problem, you will have to add a few annotiations to your User.java to avoid any unknown fields while de-serializing.
I am trying to get the request URL in a RestController. The RestController has multiple methods annotated with #RequestMapping for different URIs and I am wondering how I can get the absolute URL from the #RequestMapping annotations.
#RestController
#RequestMapping(value = "/my/absolute/url/{urlid}/tests"
public class Test {
#ResponseBody
#RequestMapping(value "/",produces = "application/json")
public String getURLValue(){
//get URL value here which should be in this case, for instance if urlid
//is 1 in request then "/my/absolute/url/1/tests"
String test = getURL ?
return test;
}
}
You may try adding an additional argument of type HttpServletRequest to the getUrlValue() method:
#RequestMapping(value ="/",produces = "application/json")
public String getURLValue(HttpServletRequest request){
String test = request.getRequestURI();
return test;
}
If you don't want any dependency on Spring's HATEOAS or javax.* namespace, use ServletUriComponentsBuilder to get URI of current request:
import org.springframework.web.util.UriComponentsBuilder;
ServletUriComponentsBuilder.fromCurrentRequest();
ServletUriComponentsBuilder.fromCurrentRequestUri();
Allows getting any URL on your system, not just a current one.
import org.springframework.hateoas.mvc.ControllerLinkBuilder
...
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(YourController.class).getSomeEntityMethod(parameterId, parameterTwoId))
URI methodUri = linkBuilder.Uri()
String methodUrl = methodUri.getPath()
Add a parameter of type UriComponentsBuilder to your controller method. Spring will give you an instance that's preconfigured with the URI for the current request, and you can then customize it (such as by using MvcUriComponentsBuilder.relativeTo to point at a different controller using the same prefix).
I want to know why spring mvc transform [""] to [null] when I use PostMan to test my API.
here is my controller:
#RequestMapping(value = "", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public #ResponseBody ResponseEntity<Object> participateRstActivities(
HttpServletRequest request, #RequestBody RstActivityFrom rstForm)
throws ServiceException {
log.info("list size:{}, frist object:{}",rstForm.getRestaurant_ids().size(), rstForm.getRestaurant_ids().get(0));
}
here is my java bean:
public class RstActivityFrom {
private List<Integer> restaurant_ids;
private int activity_id;
// omit getter & setter
}
here is my request body when I use postman to test my api:
{
"restaurant_ids":[""],
"activity_id":119129
}
and the log in controller print :
list size:1, frist object:null.
this problem makes me feel confuse, I want to know why. Thanks
Since restaurant_ids is a List and not String, Change your JSON for restaurant_ids:
{
"restaurant_ids":[],
"activity_id":119129
}
If you don't want to allow an empty String value for objects mapped from your JSON, you can look into setting the Jackson's ObjectMapper Features as described here:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
The Java API for Jackson's DeserializationConfig.Feature(s) can be found here:
http://fasterxml.github.io/jackson-core/javadoc/1.9/org/codehaus/jackson/map/DeserializationConfig.Feature.html
My method is annotated as
#RequestMapping(value = "/keepAlive", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ModelMap test(HttpServletRequest req, final ModelMap model) {
model.addAttribute("keepAlive", true);
return model;
}
when I call is using ajax using JQuery
it returns 500 server error and at server log I can see that it is looking for KeepAlive.jsp, I am using spring 3.2.10 and have jackson 2 at class path. When I debugged source code request is passed to ModelAndViewMethodReturnValueHandler rather than RequestResponseBodyMethodProcessor , It seems Model and view handler is registered before req res handler. How to solve this. Same code worked for spring 3.1.2.
Thanks