My spring controller test doesn't enter controller - spring

I am trying to test spring controller using mockMvc. There is existing functionality, so I referenced it as I was creating my test since I am new to spring controller. While existing test works fine, my test doesn't go into the spring controller I expected to. Here is my test:
#Test
public void updatePriorityStudyDispatch() throws Exception {
DispatchStudyPriorityRequest request = TestDataFactory.getDispatchStudyPriorityRequest();
mockMvc.perform(post(BASE_URL, getDispatchId(WORKSTATION_ID_VALUE, STUDY_ID_VALUE))
.accept(PowerShareMediaType.PSH_GATEWAY_STUDYDISPATCHER_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(gson.toJson(request)))
.andExpect(status().isOk());
verify(studyDispatcherService)
.updatePriority(WORKSTAION_ID, STUDY_ID, TestDataFactory.getDispatchStudyPriorityRequest());
}
and here is my controller:
#PostMapping(path = "/{dispatchid}", produces = PowerShareMediaType.PSH_GATEWAY_STUDYDISPATCHER_JSON)
public ResponseEntity<Void> updatePriority(#PathVariable("dispatchid") String dispatchId,
#Valid #RequestBody DispatchStudyPriorityRequest request) {
...
...
...
}
I had a break points on my controller and it seems like it never enters it when I run the test. My result is 400 when I am expecting 200. I am not sure what is happening.
The one that is working basically have different "request" object (different model). Same BASE_URL, same ID_VALUEs, same MediaTypes. Here is controller
#PutMapping(path = "/{dispatchid}", produces = PowerShareMediaType.PSH_GATEWAY_STUDYDISPATCHER_JSON)
public ResponseEntity<Void> dispatchStudy(#PathVariable("dispatchid") String dispatchId,
#Valid #RequestBody StudyDispatchRequest request) {
I think it has something to do with RequestBody, but this is first time I am using RequestBody with some object. I learned that there is spring functionality that converts incoming Json file to object. However, I am really new to this concept, so it is hard for me to understand the issue.

I found why it was happening. It was giving me 400 because Date() field in the DispatchStudyPriorityRequest object was in Gson format. Somehow Json format wasn't able to parse it correctly.
Gson format was something like "Jun 12, 2020" while Json format should have been "yyyy-mm-dd".

Related

Spring Boot - Why I cannot produce a String in RestController?

In Spring Boot, I am configuring the server and everything is working, except of my RestController. I dont know why:
(SO doesnt allow to include pictures yet, so here is a link)
Thats the little Controller class. Notice, that the method name in my Intellij IDEA
is grey - it is not used.
package com.example.intermediate.controller;
import org.springframework.web.bind.annotation.*;
#RestController
public class GrakaController {
#GetMapping(value = "/graka", produces = "text/plain")
#ResponseBody
public String getSimpleString() {
return "IT WORKED!";
}
}
In Postman I try to get the String by using http://localhost:8080/graka: I am getting a 200 return code, but with empty response body, no matter which response body format I choose in Postman. But I should get "IT WORKED!" back I think.
I have been struggling for some hours with that. Who got any ideas? Thanks for every tip!
try this
#GetMapping("/graka")
public String getSimpleString(){
return "IT WORKED!";
}
With Spring 4, if your Controller is annotated with #RestController, you don't need the #ResponseBody annotation.
Use ResponseEntity.
#GetMapping("/graka")
public ResponseEntity<String> getSimpleString() {
LOG.info("REST request get string value");
return new ResponseEntity<>("{\"status\" : \"IT WORKED!\"}", HttpStatus.OK);
}

How to write appropriate endpoint in Spring Boot for GET request?

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.

Get request body as string/json to validate with a json schema- Spring boot REST API

I'm trying to validate JSON (passed by a client as a request body) before it is converted into a model in Controller method.
If validation passes then return nothing, let the process continue as it was (spring boot to convert JSON into a model marked as #RequestBody). Throw error in case validation fails (everit-org/json-schema).
I tried to two way:
Implement HandlerMethodArgumentResolver, but resolveArgument() doesn't give request body details as it is already read and stored in ContentCachingRequestWrapper.
NOTE: inputStream in ContentCachingRequestWrapper doesn't have any request body details.
Using spring Interceptor. But this doesn't help me to find request body type passed in the request. As JSON schema is different for each request.
Any other approaches I can try with?
I cannot add a comment ... so ...
What kind of validation do you need? If you only want to validate the fields like length of a string or range of a number and so on. I recommend you use #Validated on controller mehtod parameter, and model:
#NotNull
#Size(min = 32, max = 32)
private String id;
controller:
#PatchMapping
public Object update(#RequestBody #Validated User user, Errors errors) {
...
}
If there is something wrong, errors.hasErrors() will return true.
edit:
OK, I did some tests, in a filter :
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
ServletInputStream inputStream = httpServletRequest.getInputStream();
byte[] a = new byte[1024];
inputStream.read(a);
System.out.println(IOUtils.toString(a));
I got a json string (a piece of request body) :
{"template":"5AF78355A4F0D58E03CE9F55AFA850F8","bd":"" ...

#RequestBody not restricting to POJO type and BindingResult hasErrors always false

I was not experiencing this problem early in development but just noticed that this was happening when debugging another problem. This happens on all REST endpoints, but below is an example:
#RestController
#RequestMapping("/editlisting")
public class EditParkingSpaceListingController {
#Autowired
ParkingSpaceRepository parkingSpaceRepository;
#Autowired
ParkingSpaceListingRepository parkingSpaceListingRepository;
#RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<String> editParking(#RequestBody ParkingSpaceListingClient pslc, BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<String>("", HttpStatus.BAD_REQUEST);
}
// Code to save pslc data to database.
Now, if I send an HTTP request with the body as
{ }
I get a 200 response and when I check MongoDB, there is a new empty document in the collection. If I send an empty body with no brackets, as expected it will return 400. If I send a body with random garbage data that does not exist in the POJO, BindingResult does not seem to pick up the error and a new blank document is still created.
You need to follow the below steps for the input document validations:
(1) Add the javax.validation package constraints (like #NotNull, #Size, etc..) to your ParkingSpaceListingClient bean class.
(2) Add #Validated annotation to your controller method, to capture the validation errors into BindingResult object.
You can look here for more details on Input Validations.

Parsing JSON request body with Spring MVC

I am using Spring 4.1 framework for developing webservices. When I return a Java object as response, it is automatically converted to JSON and delivered to client, so I assume that JSON parser is in classpath and it is configured properly. However it fails to convert the request body from JSON into Java object and client is getting a HTTP response of 400.
Here is how the webservice looks like:
public class Details{
public Details(){
}
int code;
int area;
}
#RequestMapping(value = "/api/update/{phoneNumber}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> update(#PathVariable final String phoneNumber, #RequestBody Details details)
Here is how the request looks like:
Method: Post
Content-Type: application/json; charset=utf-8
Body: {"code":0,"area":12}
If I collect the request body as string and parse it manually then it works, so it gets the valid JSON but for some reason it is not parsing it automatically. I have no clue on how to fix it. Please help. Thanks in advance.
You have package-private properties in your Details class, so they are probably not recognised by json-converter.
You have several options:
define them as public (not recommended)
provide getters and setters
if you are using jackson, you can annotate them with #JsonProperty, leaving them package-private
Finally I got the reason for this. I was using inner classes which were not static. Making those static fixed the issue.

Resources