I want to access the time-stamp in a If-Modified-Since header, so I can implement conditional GET.
Spring controllers can use the #RequestHeader annotation to indicate that Spring should pass the value of an HTTP header to a handler method as a method argument. Must the argument be a String? Or are other classes permitted? The Spring documentation implies that long values can be converted. But what is the set of classes permitted?
Will the following work (using a Date)?
#RequestMapping(method = RequestMethod.GET, headers = {"If-Modified-Since" })
public final void conditionallyRetrieve(
#RequestHeader("If-Modified-Since")final Date ifModifiedSince,
final HttpServletResponse response) {
...
}
Specifically, in your example, I think you can use DateTimeFormat to drive Spring to do the conversion:
#RequestHeader("If-Modified-Since")
#DateTimeFormat(pattern = "ThePATTERN") final Date ifModifiedSince
Related
I have an assignment to write simple GET request.
The format that is going to be typed in URL is like this:
http://localhost:8080/api/tasks/20-08-2020
Server should return TODOs for that date. I did managed to write a finder method. But not sure how to write an endpoint. This is what I have so far:
#GetMapping(value = "/{date}", consumes="application/json")
public ResponseEntity<List<Task>> getTasksByDateUsingURL(#PathVariable("date") #DateTimeFormat(pattern="dd-MM-yyyy") #Valid LocalDate dueDate){
List<Task> tasks = taskService.getAllTasksByDate(dueDate);
return new ResponseEntity<List<Task>>(tasks,HttpStatus.OK);
}
This is inside RestController class:
#RestController
#RequestMapping(value="/api/tasks")
public class TaskController {...}
I cannot hit this GET endpoint...
Workaround for your problem is to get the string as parameter and parse it manually
#GetMapping(value = "/{date}", consumes="application/json")
public ResponseEntity<List<Task>> getTasksByDateUsingURL(
#PathVariable("date")
String date
){
LocalDate dueDate = parse(date);
List<Task> tasks = taskService.getAllTasksByDate(dueDate);
return new ResponseEntity<List<Task>>(tasks,HttpStatus.OK);
}
private LocalDate parse(String stringDate) {
// TODO
}
As author said in comments:
When try to call the endpoint from browser, the mapping is not executed.
Seems like that the browser is sending request with wrong Content-Type header. Your mapping is explicitly requires only application/json value.
When try to call the endpoint from Postman, the application returns 400 status.
I could not see the body of response, but I guess the problem is #Valid annotation on the parameter. How should Spring validate the LocalDate?
So the solution is to remove consumes="application/json" from mapping or send corresponding Content-Type value
and remove #Valid annotation from parameter.
Im have #RestController and this method. how can I get any json and then select the handler depending on the method and pass it there for processing
PS. I use GSON instead of JACKSON
You can use #RequestBody in your method and take a String parameter:
public AbstractJsonResponse(#PaqthVariable String method, #RequestBody String json) {
...
}
See here: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html
Mainly the part that says:
Annotation indicating a method parameter should be bound to the body of the web request
We are facing a super strange problem: in our endpoint:
#PostMapping(value = "/send_event_to_payment_process")
#Async
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #RequestHeader(value = TENANT) String foo) {
the mapped #RequestHeader variable foo contains the vaue twice joined with a ',' ("test,test"). If we read the header programmatically using the request context:
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #Context final HttpServletRequest request) {
final String tenant = request.getHeader(TENANT);
we receive the proper value (only once: "test").
Any clues what the problem might be?!
Thank you!
You are comparing different things.
The HttpServletRequest.getHeader method always returns a single value, even if there are multiple values for the header. It will return the first (see the javadoc of the method).
Spring uses the HttpServletRequest::getHeaders method to get all the values. Which retrieves all header values and, depending on the value, return the String[] or creates a single concatenated String.
To compare the same things you also should use the getHeaders method and then you will have the same result. Which means your request contains 2 header values for the given header.
I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.
I'm trying to test a method with this signature:
#RequestMapping(value="/Employee/{id}", method=RequestMethod.PUT, consumes="application/json")
#Transactional
public #ResponseBody Map update(#PathVariable Integer id,
#RequestBody HashMap<String, Object> information) {
}
The problem is that MockMvc param attributes accept only String parameters, is there a way to pass a HashMap or an instance class object to the RequestBody as parameter?
When I try to pass a HashMap as a string, I get a MismatchException.
You need to use Jackson for this. The idea is to deserialize your objects (doesn't matter that it's HashMap) into JSON string and pass it into MockMvc.
Here is tutorial how to do that. Just search for TestClass there and take a look how it is used. Skip the unit testing of GET requests.