How to overload Spring Boot RestController methods that use #RequestBody? - java

I'm interested in overloading an #PostMapping of say, /digital/testCase, with two separate signatures, that use #RequestBody annotations to pass data, rather than #RequestParams. This is important, because all of the other questions on StackOverflow pertain to the latter. Currently, Spring Boot crashes when I try overload a function.
For those interested in seeing the code, it would look something like this
package com.example.test;
import *
#RestController
#RequestMapping(path = "/digital", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class TestController {
private static final Logger LOG = LoggerFactory.getLogger(TestController.class);
#Autowired
private testDomain testDomain;
#PostMapping("/testCase")
public ResponseEntity<Object> testCase(#RequestBody InitiateTestCase initiateTestCase,
HttpServletRequest request) {
//
...some code here
//
}
#PostMapping("/testCase")
public ResponseEntity<Object> testCase(#RequestBody InitiateTestCase[] initiateTestCases,
HttpServletRequest request) {
//
...some code here
//
}
}

You can try to add some specific params/headers to PostMapping annotation so Spring will be able to map it properly. Another way of solving such problem is to abstract your pojo used as RequestBody and not overload methods.

Related

How to write mock test case for the controller inn java

This is my controller class
#Controller
public class myClass extends BaseController {
#RequestMapping(value = "/myList", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody MyBaseVO getMyList(#RequestBody MyBaseVO requestObj) throws Exception {
requestObj.setEntity(getConfig(LanguageConstant.MY_MODULE_ID));
MyBaseVO response = (MyBaseVO) this.execute(requestObj,LanguageConstant.MY_MODULE_ID);
return response;
}
}
I wanted to write a mock test case but stuck at this point, also i'm not sure i am doing correct or not. Can anyone help me in this.
public class myClassTest{
myClass spy = Mockito.spy(new myClass ());
Mockito.when(spy.getMyList(itemRequest)).thenReturn(itemReq);
}
I am not sure how to use url myList in testcase and request and response object. I have created a json file of my request and response. Since it is using parent class therefore I am using spy for my testcase.
Since you are using Spring, you should have a look at Spring's platform Mock Test implementation for Spring MVC.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-framework

Spring Data Rest / Spring Hateoas Custom Controller - PersistentEntityResourceAssembler

I'm attempting to add some additional business logic to the auto-generated endpoints from the RepositoryRestResource. Please see the code below:
Resource:
#RepositoryRestResource(collectionResourceRel="event", path="event")
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}
Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private PagedResourcesAssembler<Event> pagedResourcesAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<PersistentEntityResource> getEvents(Pageable pageable,
PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
Page<Event> events = eventRepository.findAll(pageable);
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
}
}
I've looked at the following two stackoverflow articles:
Can I make a custom controller mirror the formatting of Spring-Data-Rest / Spring-Hateoas generated classes?
Enable HAL serialization in Spring Boot for custom controller method
I feel like I am close, but the problem that I am facing is that:
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
returns an error saying:
"The method toResource(Page<Event>, Link) in the type PagedResourcesAssembler<Event> is not applicable
for the arguments (Page<Event>, PersistentEntityResourceAssembler)".
The toResource method has a method signature that accepts a ResourceAssembler, but I'm not sure how to properly implement this and I can't find any documentation on the matter.
Thanks in advance,
- Brian
Edit
My issue was that I thought I could override the controller methods that are auto-created from #RepositoryRestResource annotation without having to create my own resource and resource assembler. After creating the resource and resource assembler I was able to add my business logic to the endpoint.
Resource:
public class EventResource extends ResourceSupport {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Resource Assembler:
#Component
public class EventResourceAssembler extends ResourceAssemblerSupport<Event, EventResource> {
public EventResourceAssembler() {
super(EventController.class, EventResource.class);
}
#Override
public EventResource toResource(Event entity) {
EventResource eventResource = createResourceWithId(entity.getId(), entity);
eventResource.setName(entity.getName());
return eventResource;
}
}
Updated Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private EventResourceAssembler eventResourceAssembler;
#Autowired
private PagedResourcesAssembler<Event> pageAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<EventResource> getEvents(Pageable pageable) {
Page<Event> events = eventRepository.findAll(pageable);
// business logic
return pageAssembler.toResource(events, eventResourceAssembler);
}
}
The thing I don't like about this is that it seems to defeat the purpose of having a RepositoryRestResource. The other approach would be to use event handlers that would get called before and/or after the create, save, delete operations.
#RepositoryEventHandler(Event.class)
public class EventRepositoryEventHandler {
#HandleBeforeCreate
private void handleEventCreate(Event event) {
System.out.println("1");
}
}
There doesn't seem to be any events for the findAll or findOne operations. Anyways, both these approaches seem to solve my problem of extending the auto generated controller methods from RepositoryRestResource.
It requires a PagedResourcesAssembler, Spring will inject one for you if you ask.
public PagedResources<Foo> get(Pageable page, PagedResourcesAssembler<Foo> assembler) {
// ...
}
In this case the resource is Foo. It seems in your case the resource you're trying to return is an Event. If that's so, I would expect your code to look something like:
private ResourceAssembler<Event> eventAssembler = ...;
public PagedResources<Event> get(Pageable page, PagedResourcesAssembler<Event> pageAssembler) {
Event event = ...;
return eventAssembler.toResource(event, pageAssembler);
}
You provide the ResourceAssembler<Event> that tells Spring how to turn Event into a Resource. Spring injects the PagedResourcesAssembler<Event> into your controller method to handle the pagination links. Combine them by calling toResource and passing in the injected pageAssembler.
The final result can be returned simply as a body as above. You could also use things like HttpEntity to gain more control over status codes and headers.
Note: The ResourceAssembler you provide can literally be something as simple as wrapping the resource, such as Event, with a Resource object. Generally you'll want to add any relevant links though.
To hack it you can use just PagedResourcesAssembler<Object> like:
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedModel<PersistentEntityResource> getEvents(
Pageable pageable,
PersistentEntityResourceAssembler persistentAssembler,
PagedResourcesAssembler<Object> pageableAssembler
) {
return pageableAssembler.toModel(
(Page<Object>) repository.findAll(pageable),
persistentAssembler
);
}

Exception using Spring Data JPA and QueryDsl via REST Controller

I'm trying to implement a controller method similar to how is documented in the latest Gosling release train of Spring Data that supports QueryDsl. I've implemented the controller as shown in the example in the docs at http://docs.spring.io/spring-data/jpa/docs/1.9.0.RELEASE/reference/html/#core.web.type-safe. Everything compiles and when I start the application (using Spring Boot 1.2.5.RELEASE), everything starts fine.
However, when I try to call my rest endpoint, I always get the following exception:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.mysema.query.types.Predicate]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:101)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:80)
My guess is that the QuerydslPredicateArgumentResolver is not being applied to the request, and thus the exception. But I see that the QuerydslPredicateArgumentResolver is registered as a bean when I query the Spring Boot manage endpoint /manage/beans. I have also ensured that #EnableSpringDataWebSupport is on my #Configuration class to no effect.
I have the controller annotated with #BasePathAwareController, since I'm using this with Spring Data REST and I want the methods to be under a similar path as the ones that Spring Data REST exposes. I also tried using #RepositoryRestController, but that didn't seem to matter. However, when using #RestController and putting it under a path that was different then the base path that Spring Data REST is using, things worked. So the question is, should it work?
The entire controller right now is:
#RestController
#RequestMapping(value = "/query")
public class AvailController
{
private final AvailRepository repo;
#Autowired
public AvailController(AvailRepository repository)
{
this.repo = repository;
}
#RequestMapping(value = "/avails", method = GET)
public #ResponseBody Page<Avail> getAvails(Model model,
#QuerydslPredicate(root = Avail.class) Predicate predicate,
Pageable pageable,
#RequestParam MultiValueMap<String, String> parameters)
{
return repo.findAll(predicate, pageable);
}
}
I had the same problem with instantiation of Predicate. In the example:
#Controller
#RequiredArgsConstructor(onConstructor = #__(#Autowired) )
class UserController {
private final UserRepository repository;
#RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model, //
#QuerydslPredicate(root = User.class) Predicate predicate, //
#PageableDefault(sort = { "lastname", "firstname" }) Pageable pageable, //
#RequestParam MultiValueMap<String, String> parameters) {
(...)
(https://github.com/spring-projects/spring-data-examples/blob/master/web/querydsl/src/main/java/example/users/web/UserController.java#L42 ) is using just #Controller and I was using #RepositoryRestController, that seems to be the reason. #RestController also works for me.
I created https://jira.spring.io/browse/DATAREST-838
I also had this issue when trying to implement a custom controller that mimics the returned value as Spring Data REST. I wanted to inject QuerydslPredicate to the controller method and got the annoying 'BeanInstantiationException'.
I found a work around for this by adding the following configuration file to my application:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE )
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("repositoryExporterHandlerAdapter")
RequestMappingHandlerAdapter repositoryExporterHandlerAdapter;
#Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
List<HandlerMethodArgumentResolver> customArgumentResolvers = repositoryExporterHandlerAdapter.getCustomArgumentResolvers();
argumentResolvers.addAll(customArgumentResolvers);
}
}
See here for reference: https://jira.spring.io/browse/DATAREST-657

Automatically add "headers" and "produces" attributes to my #RequestMapping

I implemented a REST API via Spring MVC. Here is an example of a mapping:
#RequestMapping(value = "/videos", method = RequestMethod.GET, headers = "Accept=application/json", produces = "application/json")
There are many of them, so I wonder if it's possible to factorize the headers and produces attributes, so that I don't have to specify them in each mapping, in order to lighten my code?
The best would be a custom annotation which automatically sets the two attributes, for example:
#JsonRequestMapping(value = "/videos", method = RequestMethod.GET)
But I haven't been able to implement such one...
You can put #RequestMapping also on a class next to a method (see reference guide). If you want globally available attributes put a #RequestMapping on a class, this will be merged with the one on the method.
#Controller
#RequestMapping(headers = "Accept=application/json", produces = "application/json")
public class YourController { ... }
Then your method only contains the method and url.
#RequestMapping(value="/videos", method=RequestMethod.GET)
public Object someMethod(...) { ... }
You also might want to take a look at #RestController as that also configures some defaults for your controller. Like not needing a #ResponseBody anymore on your methods.
#RestController
public class YourController { ... }

How to use #Inject in Spring MVC Controller?

I am working with Spring MVC controller. I have one of my controller as DataController.
I am thinking to add HttpServletRequest as injectable at the top of DataController class using #Inject.
#Controller
public class DataController {
#Inject
HttpServletRequest request;
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
So my question is - Is this the right way to use #Inject? I have never used #Inject before so trying to learn whether the way I am doing it is right or not? Since everytime, who is making call to processTask method, I need to grab its ipAddress whoever is calling that processTask method.
In terms of acquiring HttpServletRequest: semantically speaking, it is definitely wrong.
Reason: HttpServletRequest is an object that is created only when users send requests and is destroyed once the requested user action is completed. You simply can store it that way (from syntax angle) but you shouldn't (from semantic angle). You need to realize that the way how web application works is not exactly same as a desktop application (and don't observe them from the same angle).
Suggestion:
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow, HttpServletRequest request) {...}
In this way you will get the corresponding request each time the processTask method is called. (HttpServletRequest object is injected by #RequestMapping.)
(If you would like to preserve something through out a session, consider use a bean that is
Suggestion: #Inject private UserService userService;
(assume we have a class registered called UserService.)
You cannot "inject" the HttpServletRequest the only way to use it as far as I know is to added as a method member. Like this:
#Controller
public class DataController {
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow,HttpServletRequest request) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
look also at Spring MVC #AutoWired response not working

Categories