How to specifiy custom Request handling into spring MVC controller? - java

I have defined an class for some specific handling:
public abstract class ListProvider {
...
public abstract ResponseObject getResponse(RequestObject request) {}
}
I will create several derived class that I will define as services.
The purpose is to use it to create a json API using Jackson to deserialize the RequestObject and to serialize the ResponseObject. For example:
#Service
public class ClientListProvider extends ListProvider {
public ResponseObject getResponse(RequestObject request) {
return ...
}
I can use it in a controller like that:
#RestController
#RequestMapping("/client")
public class ClientController {
#AutoWired
ClientListProvider provider;
#PostMapping("/list")
public ResponseObject ResponseObject list(#RequestBody RequestObject request) {
return provider.getResponse(request);
}
}
But I would like to use it without the boiler plate, like:
#RestController
#RequestMapping("/client")
public class ClientController {
#PostMapping("/list")
#Provider(ClientListProvider.class)
public list() {}
}
Or maybe:
#RestController
#RequestMapping("/client")
#Provider(ClientListProvider.class,path="/list")
public class ClientController {
}
Or something like that.
Do you know if there any way? If necessary I could replace the Request/ResponseObject by HttpServletRequest/Response or something else in the ListProvider interface.

Related

Jax Rs to Rest Controller

I have a rest end point written using JAX-RS
import javax.ws.rs.core.Context
#POST
public Response something(#RequestBody MyOrderObject obj1,#Context MyObject obj2) {
}
I want to write the above rest end point using Spring Rest.What should I replace the #Context in Spring Boot ?
#RestController
class MyController
{
#POST
public #ResponseBody something(#RequestBody MyOrderObject obj1) {
}
}
The #Context is Dependency Injection featured by JAX-RS. See also: https://dzone.com/articles/jax-rs-what-is-context
In your case just inject MyObject object2 into your class as an attribue via #Autowired so you can use it later:
class MyController {
private final MyObject object2;
#Autowired
public MyController(MyObject object2) {
this.object2 = object2;
}
...
}

Create repeatative springboot controllers from configuration

Dynamic Parent Controller
#CrossOrigin("*")
public abstract class RealtimeController<T> {
public abstract RealtimeService<T> getService();
#PostMapping(value = "/find")
public ResponseEntity<Iterable<T>> find(#RequestBody T entity) {
return ResponseEntity.ok(getService().findAllByKey(entity));
}
}
Dynamic Service
public abstract class RealtimeService<T> {
public abstract RealtimeRepository<T> getRepository();
public Iterable<T> findAllByKey(T entity) {
return getRepository().findAll(
Example.of(
entity,
ExampleMatcher.matching().withIgnorePaths("_class"))
);
}
}
Child Controller1
#Api(tags = "Child1")
#RestController
#RequestMapping("/api/v1/child1")
#Slf4j
public class Child1Controller extends RealtimeController<Child1> {
#Autowired
private Child1Service child1Service;
#Override
public RealtimeService<Child1> getService(){
return child1Service;
}
}
Child Controller2
#Api(tags = "Child2")
#RestController
#RequestMapping("/api/v1/child2")
#Slf4j
public class Child2Controller extends RealtimeController<Child2> {
#Autowired
private Child2Service child2Service;
#Override
public RealtimeService<Child2> getService(){
return child2Service;
}
}
Question1:
I have 10 of these child controllers. Is there a way to dynamically create them from some config? The only difference between every child controller is the Model, tag and endpoint. My idea was to read these config from a property file and generate these controllers dynamically somehow. is it possible?
application.yaml
controllers:
- name: Child1
endpoint: /api/v1/child1
model: Child1
- name: Child2
endpoint: /api/v1/child2
model: Child2
Question2:
Is there another better way to achieve similar results without code duplication ?
You can do
#CrossOrigin("*")
#RestController
#RequestMapping("/api/v1/{childtype}")
public class RealtimeController {
public RealtimeService getService(String childtype){
return map.get(childtype);
}
#PostMapping(value = "/find")
public ResponseEntity<Iterable<Object>> find(#PathVariable("childtype") String childtype, #RequestBody Object entity) {
return ResponseEntity.ok(getServiceByType(childtype).findAllByKey(entity));
}
}
All you need now is to fill map with instances of service.

RESTful: How to dynamically extend path via interface

I have the following interfaces
#Path("/v1")
public interface IV1Api {
}
#Path("/Accounts/{AccountId}")
public interface IAccountsInstanceApi extends IV1Api {
}
#Path("/Users")
public interface IUsersListApi extends IAccountsInstanceApi {
#GET
Json listUsers();
#POST
Json createUser();
}
public UsersListResource implements IUsersListApi {
// ...
}
I was expecting my user list resource path to be /v1/Accounts/123/Users, but it is /Users. What am I doing wrong?
Sorry, but it doesn't work like this. You can do the following:
#Path(IAccountsInstanceApi.PATH)
public interface IAccountsInstanceApi extends IV1Api {
String PATH = "/Accounts/{AccountId}";
}
#Path(IUsersListApi.PATH)
public interface IUsersListApi extends IAccountsInstanceApi {
String PATH = IAccountsInstanceApi.PATH + "/Users";
#GET
Json listUsers();
#POST
Json createUser();
}

Passing variable from #Controller to #ControllerAdvice in SpringMVC

I created #ControllerAdvice that has to set me some model attribute.
#ModelAttribute
public void globalAttributes(Model model) {
model.addAttribute("pageId", PAGE_ID);
}
This is a generic example of what I need, and PAGE_ID represents some variable that actual controller has to set. Since #ControllerAdvice is running before controller, how can I declare this variable and use it in Advice? If that's even possible.
I think a better solution would been using some kind of abstract-class-pattern for your controller
public abstract class AbstractController {
#ModelAttribute
public void globalAttributes(Model model) {
model.addAttribute("pageId", getPageId());
}
abstract String getPageId();
}
public class MyController extends AbstractController {
#Override
public String getPageId() {
return "MyPageID"
}
//..your controller methods
}

Java class inheritance/interface implementation principle

I am not sure of using inheritance / interface implementation in particular situation.
In my simple Spring MVC application I have #Entity class TennisPlayer, which is inherited from abstract class Player (TennisPlayer adds some attributes).
Also I have class TennisPlayerForm, which is inherited from abstract class PlayerForm (TennisPlayerForm adds some attributes again).
User fills the form about tennis player in .jsp page and TennisPlayerForm object is used to represent filled values and then on the basis of this object is created TennisPlayer object and saved into database.
Creation of TennisPlayer object is responsibility of class TennisPlayerDbService. This class is implementation of interface PlayerService.
I have following #Controller, which handles requests:
#Controller
public class NewPlayerController {
#Resource(name="tennisPlayerService")
private PlayerService playerService;
//omitted RequestMethod.GET handler method
#RequestMapping(value = "/newplayer", method = RequestMethod.POST)
public String newplayer(Locale locale, #ModelAttribute("tennisPlayerForm") #Valid TennisPlayerForm tennisPlayerForm,
BindingResult result, RedirectAttributes redirectAttributes) {
playerService.createPlayer(tennisPlayerForm);
return "redirect:/allplayers";
}
}
Part of my source code looks like this:
public interface PlayerService {
public void createPlayer(PlayerForm playerForm);
}
#Service(value="tennisPlayerService")
public class TennisPlayerDbService implements PlayerService {
private TennisPlayerDAO dao;
#Autowired
public void setDao(TennisPlayerDAO dao) {
this.dao = dao;
}
#Override
public void createPlayer(PlayerForm playerForm) {
TennisPlayerForm tennisPlayerForm = null;
if (playerForm instanceof TennisPlayerForm) {
tennisPlayerForm = (TennisPlayerForm) playerForm;
}
else {
throw new IllegalArgumentException("Must be of type TennisPlayerForm.");
}
TennisPlayer player = new TennisPlayer();
player.setName(tennisPlayerForm.getName());
player.setSurname(tennisPlayerForm.getSurname());
player.setAge(tennisPlayerForm.getAge());
player.setRacket(tennisPlayerForm.getRacket());
player.setRanking(tennisPlayerForm.getRanking());
player.setSponsor(tennisPlayerForm.getSponsor());
player.setCoach(tennisPlayerForm.getCoach());
player.setClub(tennisPlayerForm.getClub());
dao.saveAndFlush(player);
}
}
Is it justified to use inheritance and interface implementations like this in this situation, when concrete implementation of PlayerService (TennisPlayerDbService) expects instance of particular class, although these potential classes have common parent?
Finally I solved my problem according to your comments and answers.
I deleted PlayerForm abstract class, TennisPlayerForm and mixed javax.validation and javax.persistence annotations in #Entity classes Player and Tennis Player.
Previously mentioned code now looks like this:
#Controller
public class NewPlayerController {
#Resource(name="tennisPlayerService")
private PlayerService<TennisPlayer> playerService;
//omitted RequestMethod.GET handler method
#RequestMapping(value = "/newplayer", method = RequestMethod.POST)
public String newplayer(Locale locale, #ModelAttribute("tennisPlayer") #Valid TennisPlayer tennisPlayer,
BindingResult result, RedirectAttributes redirectAttributes) {
if(result.hasErrors()) {
return "newplayer";
}
playerService.createPlayer(tennisPlayer);
MessageUtil.flash(locale, redirectAttributes, "success", "signup.success");
return "redirect:/allplayers";
}
}
public interface PlayerService<T extends Player> {
public void createPlayer(T player);
public List<T> getAllPlayers();
}
#Service(value="tennisPlayerService")
public class TennisPlayerDbService implements PlayerService<TennisPlayer> {
private TennisPlayerDAO dao;
#Autowired
public void setDao(TennisPlayerDAO dao) {
this.dao = dao;
}
#Override
public void createPlayer(TennisPlayer player) {
dao.saveAndFlush(player);
}
#Override
public List<TennisPlayer> getAllPlayers() {
return dao.findAll();
}
}
Normally your service does not need to know you are working with a form. Your form is purely created to be the model in the model-view-controller architecture of your webpage. (your jsp being the view and your controller being the c-part)
Are you also planning on using other types of players than a TennisPlayer? If not it all seems like premature optimisation and you should keep it as simple as possible.

Categories

Resources