Spring responds 400 with array query param - spring

I have the following spring boot #RestController request mapping:
#RequestMapping({"/api/blog"})
#RestController
public class BlogController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public Iterable<Blog> filterBy(
#RequestParam(required = false, name = "filter") String filterStr,
#RequestParam(required = false, name = "range") String rangeStr,
#RequestParam(required = false, name="sort") String sortStr) {
...
}
}
The request should look like so:
GET http://my.api.url/api/blog/test?sort=['title','ASC']&range=[0, 24]&filter={title:'bar'}
However providing any of the array query parameters (range and/or sort) causes a response of 400 with no details other than: "HTTP Status 400 - Bad Request"
Making a request with only the filer query parm works. Adding range and/or sort with null values works. Soon as i add the brackets [] it appears to fail.
I have tried to add the following Exception handler to debug the problem to both the controller and a ControllerAdvice class:
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handle(HttpMessageNotReadableException e) {
logger.warn("Returning HTTP 400 Bad Request", e);
}
However this does not get triggered.
I have a suspicion that something is happening in the framework causing the 400 before it even gets to the controller.
Any help is appreciated.

Try defining your parameters as Lists and dont use brackets.
#RequestMapping(value = "/test", method = RequestMethod.GET)
public Iterable<Blog> filterBy(
#RequestParam(required = false, name = "filter") List<String> filterStr,
#RequestParam(required = false, name = "range") List<String> rangeStr,
#RequestParam(required = false, name = "sort") List<String> sortStr) {
filterStr.forEach(s -> System.out.print(", "+ s));
System.out.println();
rangeStr.forEach(s -> System.out.print(", "+ s));
System.out.println();
sortStr.forEach(s -> System.out.print(", "+ s));
System.out.println();
return new ArrayList<>();
}
// test url with mockmvc
#Test
public void filterBy() throws Exception {
mockMvc.perform(get("/test?filter=1,2,3,4&range=5,7,8&sort=desc"))
.andExpect(status().is2xxSuccessful());
}
#Test
public void filterBy() throws Exception {
mockMvc.perform(get("/test?filter=1&filter=2&filter=3&filter=4&range=[5,7,8]&sort=desc"))
.andExpect(status().is2xxSuccessful());
}
For me the first test prints:
, 1, 2, 3, 4
, 5, 7, 8
, desc
The second test prints:
, 1, 2, 3, 4
, [5, 7, 8] // brackets dont seem to work that good
, desc

you have to URL encode your parameters! when you test, you should URL encode the filter, the range and the sort parameters. Try the following:
http://my.api.url/api/blog/test?sort%3D%5B%27title%27%2C%27ASC%27%5D%26range%3D%5B0%2C+24%5D%26filter%3D%7Btitle%3A%27bar%27%7D

Related

Order of different Spring controller methods by params

There are two methods in the following controller with different params values.
#RestController
#RequestMapping("/api/v1")
public class ToyApiController {
#GetMapping(value = "/toys", params = {"toySize=large"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listLargeToys(ListToyCommand listToyCommand) {
// ...
}
#GetMapping(value = "/toys", params = {"country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listToysOfCountry(ListToyCommand listToyCommand) {
// ...
}
}
The API below is expected to hit the listLargeToys method. The behavior is correct.
GET /api/v1/toys?toySize=large&max=10
The API below is expected to hit the listToysOfCountry method. However, the listLargeToys method is hit.
GET /api/v1/toys?country=france&toySize=large&max=10
The code in this post is simplified from actual code. I know that the two methods can be combined into a single method that checks the toySize and country parameters and returns different values accordingly. I'm just wondering whether the listToysOfCountry method can be hit by some tiny changes, e.g. specifying parameters like params without combining the two methods.
GET /api/v1/toys?country=france&toySize=large&max=10
I guess you need some like this:
#RequestMapping(
value = "/toys",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody ResponseEntity<String> toys(
#RequestParam(value = "toySize", required = false) String toySize,
#RequestParam(value = "country", required = false) String country,
#RequestParam(value = "max", required = false) Integer max) {
if(null != max && max == 10) {
System.out.println("You send 10 dude.");
}
return new ResponseEntity<String>("{ \"toySize\": "+ toySize +", \"country\": "+ country +", \"max\": "+ max +"}", HttpStatus.OK);
}
Using "required = false", you make param optional :)
By adding a !country in params for listLargeToys API, GET /api/v1/toys?country=france&toySize=large&max=10 no longer goes into listLargeToys API. Instead, it goes into listToysOfCountry as expected.
#RestController
#RequestMapping("/api/v1")
public class ToyApiController {
#GetMapping(value = "/toys", params = {"toySize=large", "!country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listLargeToys(ListToyCommand listToyCommand) {
// ...
}
#GetMapping(value = "/toys", params = {"country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listToysOfCountry(ListToyCommand listToyCommand) {
// ...
}
}

how to access GetMapping notation from postman with HttpServletRequest

I have a spring boot controller but I don't know how to access the GetMapping notation through postman application. This is my controller:
#GetMapping
public ResponseEntity<dataTableDTO> getProject(HttpServletRequest request, int draw) throws Exception {
//... do what needs to be done
List<ProjectEntity> objProj = (List<ProjectEntity>) projectRepository.findAll();
List<String> slist = new ArrayList<String>();
for(ProjectEntity d : (List<ProjectEntity>)objProj){
slist.add(String.valueOf(d.getCustomerId()));
}
String listCustId = StringUtils.collectionToCommaDelimitedString(slist);
List<CustomerDTO> objCust = (new CustomerDAO()).getCustomer(listCustId, request.getHeader("Authorization"));
List<ProjectDTO> objProjDTO = new ArrayList<ProjectDTO>();
for(ProjectEntity d : (List<ProjectEntity>)objProj){
String name = "";
for(CustomerDTO c : objCust){
if(c.getId() == d.getCustomerId()){
name = c.getFirstName() + " " + c.getLastName();
}
}
objProjDTO.add(new ProjectDTO(d.getId(), d.getCustomerId(), name, d.getName(), d.getType()));
}
dataTableDTO data = new dataTableDTO(draw, objProjDTO.size(), objProjDTO.size(), objProjDTO, null);
return new ResponseEntity<dataTableDTO>(data, HttpStatus.OK);
}
I just want to know how to access the GetMapping notation through postman. I already try but i got error
error image
Put a #RequestParam annotation on your draw variable?
#GetMapping
public ResponseEntity<dataTableDTO> getProject(HttpServletRequest request, #RequestParam(name="draw") int draw) throws Exception {...}

How test Post request with custom object in content type application/x-www-form-urlencoded?

I have controller:
#PostMapping(value = "/value/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateSettings(final Dto dto) {
System.out.println(">>> " + dto);
return "template";
}
Controller works if I send request across chrome window. But when I write test for this method I get problem. Not converted object, value not inserted.
Test:
#Test
#WithMockUser(username = FAKE_VALID_USER, password = FAKE_VALID_PASSWORD)
public void test_B_CreateDtoWithValidForm() throws Exception {
final Dto dto = new Dto();
dto.setId("value");
dto.setEnabled("true");
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content(dto.toString()))
.andDo(print());
}
Output is >>> Dto{id=null, enabled=false}
How test Post request with custom object in content type application/x-www-form-urlencoded?
In this case you don't need to use content, but instead you need to use param in this way:
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "value")
.param("enabled", "true"))
.andDo(print());

Redirected URL = http://localhost/login in controller method that requires authentication

I am testing a controller that requires authentication , I have performed the authentication to the best of my knowledge and I still keep getting redirection ,
After debug it shows that the login works so I don't get why it continues to show this below
Redirected URL = http://localhost/login
Cookies = []
java.lang.AssertionError: No ModelAndView found
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:35)
This is spring boot and here is my test and controller method.
#RequestMapping("/bookDetail")
public String bookDetail(
#PathParam("id") Long id, Model model, Principal principal
) {
if (principal != null) {
String username = principal.getName();
User user = userService.findByUsername(username);
model.addAttribute("user", user);
}
Book book = bookService.findOne(id);
model.addAttribute("book", book);
List<Integer> qtyList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
model.addAttribute("qtyList", qtyList);
model.addAttribute("qty", 1);
return "bookDetail";
}
And test.
#Test
public void checkBookDetail() throws Exception {
LoginStatus loginStatus = securityService.login("user", "78fa095d-3f4c-48b1-ad50-e24c31d5cf35");
bookService = createMock(BookService.class);
ReflectionTestUtils.setField(homeController, "bookService", bookService);
Book book = new Book();
book.setId(1L);
expect(bookService.findOne( anyLong())).andReturn(book);
replay(bookService);
mockMvc
.perform(get("/bookDetail")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
.param("id", "1"))
.andExpect(model().attributeExists("book"))
.andExpect(model().attributeExists("qty"))
.andExpect(model().attributeHasErrors("user"))
.andExpect(model().attributeExists("qtyList"))
.andExpect(content().contentType(MediaType.TEXT_HTML))
.andReturn();
}
What am I missing here?

Controller Testing For SPRING-MVC

I am getting error in my controller Saying Null Pointer Exception while When I don't perform the testing. Everything works fine.
Controller :
#RequestMapping(value = "/studentinsection/{sectionId}", method = RequestMethod.GET)
public ModelAndView studentInSectionForm(#ModelAttribute("studentInSectionFormData") StudentInSectionForm studentInSectionFormData,
#PathVariable Integer sectionId,
ModelMap model) {
ArrayList<StudentInSections> studentInSectionList = (ArrayList<StudentInSections>)
studentInSectionsService.retrieveAllStudentInSections(sectionId, 1);
StudentSection studentSection = studentSectionService.retrieveStudentSection(sectionId);
logger.info("section Name is:" + studentSection.getSectionName());
ArrayList<User> userList = new ArrayList<User>();
for (StudentInSections studentInSections : studentInSectionList) {
String studentName =
(userService.retrieveUserName(studentInSections.getStudentId(), 1));
User users = userService.retrieveUser(studentName);
userList.add(users);
}
logger.info("sectionId is " + sectionId);
ArrayList<User> allStudents = (ArrayList<User>)
userService.retrieveAllStudents();
studentInSectionFormData.setStudentInSectionList(studentInSectionList);
model.addAttribute("studentList", allStudents);
model.addAttribute("userList", userList);
model.addAttribute("studentSectionName", studentSection.getSectionName());
model.addAttribute("studentSectionId", studentSection.getSectionId());
return new ModelAndView("studentinsection", "studentInSectionFormData", studentInSectionFormData);
}
Testing is as follow:
#Test
public void testStudentInSectionForm() throws Exception {
mockMvc.perform(get("/studentinsection/1"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("studentinsection"));
}
this is passing everything into the controller fine even sectionId is getting printed 1 in logger than also studentin sectionList returns nullMointerException. help me to resolve my problem.. Thanx
It slooks like the context is not being loaded correctly. What is the exception stacktrace.
You can also view the request if you do :
mockMvc.perform(get("/studentinsection/1"))
.andExpect(status().isFound())
.andDo(print())

Resources