Spring Boot MVC for the same endpoint sending two different responses (with a little difference) - spring

I have a not-a-common requirement where I am two different type of consumers for my microservices. One type of consumer is okay with the type-of-response that I am sending them, whereas the other consumer has a requirement where we have to follow their structure (its pretty strict on this).
Lets say I have a StudentController
#RestController
#RequestMapping("/student")
public class StudentController {
#GetMapping("/{name}")
public Student getStudent(#PathVariable String name) {
return Student.builder()
.name(name)
.subjects(List.of("Maths", "English"))
.dateJoined(LocalDate.now().toString())
.build();
}
}
This is alright as one my consumer is accepting my response, where my response looks like this:
{"name":"smit","subjects":["Maths","English"],"dateJoined":"2020-04-26"}
However, the another consumer says that you should sending me the SAME object in a another from something like below:
#RestController
#RequestMapping("/wrapper")
public class WrapperController {
#Autowired
private StudentController studentController;
#GetMapping("/{name}")
public WrapperResponse getStudent(#PathVariable String name){
return WrapperResponse.builder()
.responseTimeStamp(LocalDateTime.now().toString())
.data(studentController.getStudent(name))
.build();
}
}
The below is the output of the above controller.
{"data":{"name":"smit","subjects":["Maths","English"],"dateJoined":"2020-04-26"},"responseTimeStamp":"2020-04-26T01:11:32.986"}
Summary: WrapperController is internally calling StudentController and then wrapping the response in the custom "WrapperResponse" class and then sending that as a response.
Problem: As of now it does solve the problem but I have many such controllers and in my different microservices. So I do not want to rewrite the "WrapperController" for each controller and each microservice.

This is very much opinion-based, but here's how I would refactor this:
Create a Student service (StudentService) that creates the Student,
rather than doing that in the Controller.
Push the code for building a WrapperResponse into the WrapperResponse class itself - either as a static 'factory' method, or as constructor. Have the method take a Student as a parameter.
For each different kind of 'Wrapper' object that you want to supply, have your controller method call the StudentService to get the base Student class, and then construct the Wrapper object by passing the Student to the factory method or constructor of the Wrapper object.
So, your WrapperController might end up looking like this:
#RestController
#RequestMapping("/wrapper")
public class WrapperController {
#Autowired
private StudentService studentService;
#GetMapping("/{name}")
public WrapperResponse getStudent(#PathVariable String name){
return new WrapperResponse(studentService.getStudent(name));
}
}

Related

How can I properly override a method declared in an abstract generic restcontroller?

I'm having some trouble implementing a function over some pre-existing code.
Other programmers working on this project previously defined a genric abstract "restcontroller" (it's not actually annotated as #RestController but it's meant to be extended by classes with that annotation)
public abstract class AbstractController<T extends AbstractEntity, R extends JpaRepository<T, Integer>> {
#GetMapping(value = "/getall")
public Paging<T> getAll(#RequestParam Integer itemsPerPage,
#RequestParam Integer pageIndex,
#RequestParam Map<String, String> filters,
#Autowired Consumer consumer) {
//Fetch entities of type T from repository R and return them
}
//other generic crud operations
}
This class is usually extended by concrete controllers that simply define other operations on their specific types, but do no alter generic crud operations.
What I want to do is extend this class, but override the getAll method, like this:
#RestController
#RequestMapping("/api/tasks")
public class TaskController extends AbstractController<Task, TaskRepository> {
#Override
public Paging<Task> getAll(Integer itemsPerPage, Integer pageIndex, Map<String, String> filters, Consumer consumer) {
LoggerFactory.getLogger(LazyTaskController.class).log("function called successfully!");
Paging<Task> paging = super.getAll(itemsPerPage, pageIndex, filters, consumer);
//do things with return value before returning
return paging;
}
}
If I call BASEURL/api/tasks/getall?itemsPerPage=25&pageIndex=0 without overriding the getAll method, the parameters are wired correctly (the Map contains two values, itemsPerPage and pageIndex, as expected, and consumer contains a concrete implementation of the intercace Consumer).
However if I do override it, the Map for some reason contains two values, one with key "consumer" and type Proxy, and another with key "org.springframework.validation.BindingResult.consumer" and value of type BeanPropertyBindingResult; and consumer contains a Proxy.
I suppose the #Override interferes with the autowiring of Consumer, but I can't figure out how to properly achieve what I have in mind (manipulating the results of getAll before returning them).
Thank you in advance
Nevermind, I solved it.
The problem with the Map was solved by adding #RequestParam and #Autowired annotations to the overridden method parameters as well.
The problem with the Consumer concrete type was somehow solved by applying a custom annotation that I found on another class in the codebase, I'm still not sure about what that annotation does but at least I know what to look for now.

Thread safe on Inheritance of spring boot controller

I am writing an API using Spring Boot, and I have a abstract controller to hold the shared logic among several controllers. Now I want to add a warning field:
public abstract class BaseController<T> {
public List<String> warnings;
#RequestMapping(method = POST)
public Response create(HttpServletRequest request,HttpServletResponse response) {
warnings = new ArrayList<>();
if (something bad from T) {
warning.add("bad thing happens");
}
return createRespone(warnings);
}
(createReponse is uesd to create custom reponse)
And I have several different controller inherited from it
#RestController
#RequestMapping("/{area}/blah")
public class BlahController extends BaseController<Blah> {
}
For the warning field, will it be shared several different children controller, or will only one instance alive? If controller A and controller B are both inherited from BaseController and tried to modify warning, is it thread safe?
The warning field is not shared. Your code is equivalent to:
BaseController<blah> blahController = new BlahController();
BaseController<noh> nohController = new NohController();
Having state in a controller is against the REST concepts.

How can I put an instance of an object as session attribute in a Spring MVC project?

I am working on a Spring MVC application and I have the following problem.
I have this RegistrazioneInfo class that contains some information inserted into a form by the user:
public class RegistrazioneInfo {
#NotNull
#Size(min=16, max=16)
private String codiceFiscale;
String gRecaptchaResponse;
public String getCodiceFiscale() {
return codiceFiscale;
}
public void setCodiceFiscale(String codiceFiscale) {
this.codiceFiscale = codiceFiscale;
}
public String getgRecaptchaResponse() {
return gRecaptchaResponse;
}
public void setgRecaptchaResponse(String gRecaptchaResponse) {
this.gRecaptchaResponse = gRecaptchaResponse;
}
}
Then I have this controller class:
#Controller
public class RegistrazioneController extends BaseController {
private RegistrazioneInfo registrazioneInfo;
...............................................
...............................................
...............................................
}
that contains some methods handling request towards some resources.
Ok, my problem is that I want to use an instance of the previous RegistrazioneInfo class as session attribute by the use of the #SessionAttributes Spring annotation as shown here: http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-sessionattrib
My problem is, in the previous example do something like this:
#SessionAttributes("pet")
public class EditPetForm {
// ...
}
So what exactly is pet? I think that it is something like an id that identify the object that have to be used as a session attribute or something like this. How can I say to put an instance of my RegistrazioneInfo as session attribute?
#SessionAttributes is declared in a Controller Class (#Controller), so on the class level.
Pet is an Bean Object that persist in HttpSession
From the documentation:
This will typically list the names of model attributes which should be transparently stored in the session or some conversational storage, serving as form-backing beans. Declared at the type level, applying to the model attributes that the annotated handler class operates on.
(emphasis is mine)
Also note that, as indicated in the documentation, you should not use that for "non temporary" elements.

Can I "inject" values from message resources into model objects before implicit Jackson serialisation?

I have a REST API built with Spring Boot / Spring MVC, using the implicit JSON serialization via Jackson.
Now, just before the implicit serialization, I would like to "inject" some UI texts from message resources into the objects that Jackson converts into JSON. Is there some neat, simple way to do this?
As a much simplified example, below I'd like to set Section title to a user-visible value, based purely based on its SectionType.
(Sure, I could hardcode the UI texts in SectionType, but I'd rather keep them separate, in resource files, because it's cleaner, and they might be localised at some point. And I can't autowire MessageSource in the entities / model objects which are not Spring-managed.)
#Entity
public class Entry {
// persistent fields omitted
#JsonProperty
public List<Sections> getSections() {
// Sections created on-the-fly, based on persistent data
}
}
public class Section {
public SectionType type;
public String title; // user-readable text whose value only depends on type
}
public enum SectionType {
MAIN,
FOO,
BAR;
public String getUiTextKey() {
return String.format("section.%s", name());
}
}
Somewhere in a #RestController:
#RequestMapping(value = "/entry/{id}", method = RequestMethod.GET)
public Entry entry(#PathVariable("id") Long id) {
return service.findEntry(id);
}
UI texts that I'd like to keep separate from code (messages_en.properties):
section.MAIN=Main Section
section.FOO=Proper UI text for the FOO section
section.BAR=This might get localised one day, you know
And what I'd like to do in a Spring-managed service/bean somewhere (using Messages, a very simple helper wrapping a MessageSource):
section.title = messages.get(section.type.getUiTextKey())
Note that if I call entry.getSections() and set the title for each, it will not affect the JSON output, since the Sections are generated on the fly in getSections().
Do I have to go all the way to custom deseriazation, or is there a simpler way to hook into the model objects just before they get serialized by Jackson?
Sorry if the question is unclear; I can try to clarify if needed.
As I said in the comment you can write an Aspect around every controller method that returns Section.
I wrote a simple example. You have to modify it with the message source.
Controller:
#RestController
#RequestMapping("/home")
public class HomeController {
#RequestMapping("/index")
public Person index(){
Person person = new Person();
person.setName("evgeni");
return person;
}
}
Aspect
#Aspect
#Component
public class MyAspect {
#Around("execution(public Person com.example..*Controller.*(..))")//you can play with the pointcut here
public Object addSectionMessage(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = pjp.proceed();
Person p = (Person) retVal; // here cast to your class(Section) instead of Person
p.setAge(26);//modify the object as you wish and return it
return p;
}
}
Since the aspect is also a #Component you can #Autowire in it.

spring interface as #ModelAttribute param

I have UsersController with method:
#RequestMapping(value={"/new"}, method=RequestMethod.GET)
public String showCreationForm(#ModelAttribute User user){
return "user_registration_form";
}
which displays registration form. I want to keep modularity (would be nice to use this controller in some other project) in my project so User is an interface and there is its implementation - UserImpl. The problem is that Spring cannot instatiate User interface. Is there a way to configure spring to use some default implementation of User?
You can provide an object to be populated with request data using #ModelAttribute-annotated method:
#ModelAttribute
public User createUser() {
return new UserImpl();
}
Create a simple class that implements the interface minimally. It is the same idea as an interface, but it is a class. It does not contain any of your logic or validation or anything else. It is just the simplest implementation of the interface, call it UserSimple, and it implements your interface. It is called a Data Transfer Object.
public class UserSimple implements User {
String name;
String address;
//getters and setters only
}
Add a converter that copies the real properties of the UserImpl into the UserSimple.
#Component
public class ImplToSimpleConverter
implements Converter<UserImpl, UserSimple> {
#Override
public UserSimple convert(UserImpl source) {
UserSimple target = new UserSimple();
BeanUtils.copyProperties(source, target);
return target;
}
}
Use UserSimple in the handler.
#RequestMapping(value={"/new"}, method=RequestMethod.GET)
public String showCreationForm(#ModelAttribute UserSimple user){
return "user_registration_form";
}
This allows you to keep the code generic. Adding a different converter is all you would have to do to use the same class in a different application.

Resources