I am working with Spring Framework 4.3.2
About Rest for POST and PUT the following situation happens.
For POST I have the following:
#PostMapping(consumes={MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<Void> saveOne(#Validated #RequestBody Persona persona){
Observe the method has declared #Validated.
I need do the validation and return the error text message according the Locale sent it by the client/user.
I can do it through Spring MVC Test, for that I have the following:
resultActions = mockMvc.perform(post(uri).contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML)
.locale(locale)
.content(...content...))).andDo(print());
Observe the .locale(locale) part.
Until here about JUnit through #Parameters I am able to send a list of Locales and thus see the error message in different languages. Until here all go how is expected
Now, the other way to access a Rest controller is through RestTemplate
I have the following:
RequestEntity<Persona> requestEntity = RequestEntity.post(uri)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(personaValid);
....
ResponseEntity<GenericCollection<ValidatedFieldErrorReport>> responseEntity_
= restTemplate.exchange(requestEntity, parameterizedTypeReference);
Sadly RequestEntity through the post does not support .locale(locale)
Even If I add .header("Accept-Language", locale.toString()) it does not work, same consideration for .header("locale", locale.toString()).
Note: I can confirm when I print the RequestEntity object it sends the expected Locale but in the server: it ignores the 'locale' sent it (how if it never has been not sent it from the beginning) and uses the default Locale available in the server. I am confused with this approach.
I want keep the RequestEntity and ResponseEntity objects. Their API are fluent.
Therefore what is the correct way?. I am with the impression an extra configuration in either client or server is need it in some place.
For RequestEntity you should use:
.header(HttpHeaders.ACCEPT_LANGUAGE, locale.toLanguageTag());
Related
I have a rest api set up at api/books, and one can send a new book object there, and it will be added to the database. The guestion is, how can I correctly catch what is being POST'ed, so one can for example, validate what is being sent?
#RequestMapping(value="/api/books", method = RequestMethod.POST)
public String bookSavePost(#RequestBody Book book) {
bookRepository.save(book);
return "redirect:/api/books";
}
This works, as in it saves the book and I can catch what the user sends, but with this enabled, it overrides the default REST method, which doesn't allow one to actually view the api anymore. If I change this particular method to GET, it returns a string "redirect:/api/books", so it doesn't even redirect anything. Is there some way to actually redirect it to the rest api endpoint?
You can write your own reuquest Interceptor .
Spring provides HandlerInterceptor class :
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html
Here is a quick sample how to do this:
https://www.baeldung.com/spring-mvc-handlerinterceptor
A redirect requires three two things to be successful: A HTTP code of 301 or 302 AND a location header that includes the endpoint to which you want the client to visit.
E.g., in the headers section you should have
location: '/correct/endpoint'
We are building a RESTful API with several #RestControllers which return all kinds of objects including byte arrays (actually application/pdf).
When an exception occurs we handle those with #RestControllerAdvices which return a custom ErrorView object. Still spring insists of rendering those as application/pdf which of course is not possible. The client is sending application/json in the accept header but this does not seem to help. Any pointers how to fix this?
Ok, I made a mistake which I think I should share.
Basically most of the time the content type negotiation between Spring and the client works like a charm. If the client accepts (as is the case for our app) "application/pdf, application/json" then spring will try to work it our for all responses AKA return values from #RestController functions. Except, of course, when you explicitly tell Spring to only produce a certain type of response e.g. with
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE))
After changing this to
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE, MediaType.APPLICATION_JSON_VALUE))
the ErrorView gets rendered as JSON as intended.
What's the difference between read header data from #RequestHeader annotation vs HttpServletRequest?
The advantage of using Spring #RequestHeader is that it will automatically throw an exception like
HTTP Status 400 - Missing request header 'X' for method parameter of type, if the header is NOT sent in the input request (by setting required=true). An example usage shown below:
#RequestMapping(method=RequestMethod.GET)
public String users(#RequestHeader(required=true)String myHeader, Model model) {
//your Code
}
You can also set the default value for the header if you use #RequestHeader, you can refer here
We need to manually check the condition for header value !=null, throw the exception or set the default value, if you use HttpServletRequest, which will make your code verbose.
There is no difference in Performance. It is more convenient to use #RequestHeader than HttpServletRequest.getHeader().
This is similar to #RequestParam and HttpServletRequest.getParameter().
Spring has done the work of converting the request Headers, Parameters to method Parameters so that you can avoid boilerplate code.
There is a Spring written MethodArgumentResolver named RequestHeaderMethodArgumentResolver which in fact uses HttpServletRequest.getHeader() to get the header.
I'm experimenting with Spring 4 WebSocket STOMP application. Is there a way to explicitly specify content type of the returned message produced by a handler? By default the handler below produces application/json and processed by corresponding message converter.
#Controller
public class ProductController {
#MessageMapping("/products/{id}")
public String getProduct(#DestinationVariable int id) {
return getProductById(id);
}
}
I'm looking for something like #RequestMapping(produces = "text/xml") in Spring MVC.
UPDATE (reply to Rossen's answer):
Ideally I would like to be able to return both formats depending on what the user asks for. But if I have to choose, I would say XML and almost never JSON (XML is just an example, we use binary format). I went the second way you suggested - configuring custom converters instead of the default ones.
I have implemented custom MessageConverter extending AbstractMessageConverter. In the constructor I've registered appropriate supported MimeType.
Then I've registered my custom converter by overriding WebSocketMessageBrokerConfigurer's configureMessageConverters, and return false from the method to not add default converters.
As soon as my controller returns the value I get NPE in SendToMethodReturnValueHandler's postProcessMessage. This happens because CompositeMessageConverter contains only a single converter - my custom one. But my converter fails AbstractMessageConverter's supportsMimeType check and AbstractMessageConverter's toMessage returns null. This null causes the exception in postProcessMessage.
As the workaround I can register additional default MimeType application/json for my custom converter. But it looks like too dirty to me.
There is nothing like the produces condition. The only way to do this right now is to inject SimpMessagingTemplate and then use the convertAndSend method variation that takes a MessagePostProcessor. It could be made simpler. What's your use case? Are you using JSON primarily and need to use XML in a few places? Or do you need to use XML primarily? If it is the latter you can simply configure the converters to use instead of the default ones.
I see that you can change the headers changing the Message converter.
For example:
SockJsClient sockJsClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
..
stompClient.setMessageConverter(new StringMessageConverter());
or
stompClient.setMessageConverter(new SimpleMessageConverter());
when you use StringMessageConverter then the Java client add an content-type header.
GenericMessage [payload=byte[33], headers={simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={destination=[/app/chatchannel], content-type=[text/plain;charset=UTF-8], content-length=[33]}, simpSessionAttributes={ip=/127.0.0.1:59629}, simpHeartbeat=[J#147a2bf, contentType=text/plain;charset=UTF-8, lookupDestination=/chatchannel, simpSessionId=79431feb8b5f4a9497492ccc64f8965f, simpDestination=/app/chatchannel}]
But if you use SimpleMessageConverter, the content-type header is not add.
this is a clue about what you want to do?
Check this question, I put some code that could help you:
How configure the Spring Sockjs Java Client message converters
I have Spring MVC application with this controller method.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addNumber(#RequestParam(value="number", required=false) Long number) {
...
return "redirect:/showAll/";
}
In my JSP I have a standard HTML form which is posting a value named "number" to the controller method above. However, if I leave out the value (do not enter anything into the text field) and POST the data to the controller, before the controller method is called my browser shows
HTTP Status 400 - Required Long parameter 'number' is not present
although the controller method annotation clearly defines the "number"-parameter as not required.
Does anyone have a slight idea of what could be going on?
Thank you.
PS: The exception that is being thrown is as follows:
org.springframework.web.bind.MissingServletRequestParameterException: Required Long parameter 'number' is not present
EDIT: This is a Spring 3.2.3.RELEASE bug ( see here). With version 3.1.4.RELEASE I do not have this problem anymore.
I came across the same situation, and this happens when your parameter is present in the request with an empty value.
That is, if your POST body contains "number=" (with empty value), then Spring throws this exception. However, if the parameter is not present at all in the request, it should work without any errors.
My problem was that some of the headers in a request I was sending with Postman were not present (were unchecked):
When I checked back the Content-Length header, the request worked fine (200 OK response).