Avoid duplicate code in Controllers and service - spring

I am using Spring framework.
I wrote some pretty long code to save some results.
So later it turned out in other controllers I will also need this code. Just with small differences for example returning some other strings.
So of course every controller will have its own mapping. So the parameters will be duplicate anyway.
But now for the code inside the mapping method.
I was thinking putting the code in the service of the original controller. Then the other controllers will call this service. The service of course will have plenty of parameters + the slight differences between the controllers. Or should I make like a general service, have good documentation there because of course the methods there will be general and later I should know what they were for.
#PostMapping("/testcase") public RedirectView saveResult(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity, RedirectAttributes redirectAttributes , #RequestParam(required = false) String version , #RequestParam(required = false,defaultValue = "0") String page, #RequestParam(required = false) String operation, Authentication authentication,Locale locale)
{ // here comes long code, which will be used also in other controllers ;
}

If all the controller mappings have the same signature you can create a parent class with the common implementation.
Something like this:
public abstract class BaseAbstractController {
// specific logic per controller
abstract String specific();
public RedirectView save(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity,
RedirectAttributes redirectAttributes, #RequestParam(required = false) String version,
#RequestParam(required = false, defaultValue = "0") String page, #RequestParam(required = false) String operation,
Authentication authentication, Locale locale) {
// here comes long code, which will be used also in other controllers ;
String specific = specific();
}
}
#Controller
public class TestController extends BaseAbstractController {
#Override
String specific() {
return "something"; // here goes your specific logic;
}
#PostMapping("/testcase")
public RedirectView saveResult(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity,
RedirectAttributes redirectAttributes, #RequestParam(required = false) String version,
#RequestParam(required = false, defaultValue = "0") String page, #RequestParam(required = false) String operation,
Authentication authentication, Locale locale) {
return save(model, testResultEntity, redirectAttributes, version, page, operation, authentication, locale);
}
}

Related

Spring boot : REST API behaviour inconsistent post version upgrade

I have issue after upgrading to Spring Boot 2.3.0.RELEASE from 1.5.10.RELEASE. Our controller API looks like -
#RequestMapping(value = "/card", method = RequestMethod.GET)
public CardRespDTO getCards(#RequestParam String profileId, #RequestParam(required = false) String banner, #RequestParam(required = false) String paymentGatewayVersion);
Consumer were able to call this API by not passing profileId param but by just providing some USER_ID header. But post the version upgrade, those calls are failing with below error -
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'profileId' is not present
Can someone please help identifying the issue here? We can't ask consumer to make a change.
Marking profileId as not required should do the trick:
#RequestMapping(value = "/card", method = RequestMethod.GET)
public CardRespDTO getCards(#RequestParam(required = false) String profileId,
#RequestParam(required = false) String banner,
#RequestParam(required = false) String paymentGatewayVersion)

Extract all parameters from a Spring controller

Given a Spring controller:
#GetMapping("/test/{date}")
public String details(
#PathVariable LocalDate date,
#RequestParam(required = false) SortOptions sort,
Model model) {...}
I would like to get a Map with all the parameter: {'date': date, "sort": sort}
Map<String, Object> map = getCallingParameters(); // <----
I would like a general solution, not for this particular controller.
You need:
#GetMapping("/test/{date}")
public String details(
#RequestBody Map<String,Object> body) {...}
Note, that if you change your idea you can also use #RequsetBody with wrapper objects (which I suggest to consider).

Spring MVC forward appending request parameter values comma separated when we have same parameter name for topRequest and forward request

Spring MVC forward appending request parameter values comma separated when we have same parameter name for topRequest and forward request
#RequestMapping(path = "/details")
public ModelAndView details(#ModelAttribute final DetailsForm detailsForm){
//DetailsForm contain a parameter called destinationId with value 1234
final ModelAndView mav = new ModelAndView();
//Some logic to get targeted destinationId (7890) using destinationId (1234) from detailForm
mav.setViewName("forward:/search?destinationId=7890");
return mav;
}
#RequestMapping(path = "/search")
public ModelAndView details(#ModelAttribute final SearchForm searchForm){
//Here I tried to get destinationId from model-attribute searchForm
final Integer destinationId = searchForm.getDestinationId();
//Then it returned me 1234,7890
}
Can someone please help me out how to resolve this. I want to get only 7890.
I am interested in the answer also. I also hit this problem hacked it by adding a method:
private String getLastPartFromFormValue(final String value) {
if (value == null)
return null;
String[] parts = value.split(",");
return parts[parts.length -1];
}
Just for sake of knowledge.
If you have a method, and you have a query param named thing and have an object annotated with #ModelAttribute and, in that object you have a field with the same name of your query param, you can expect that behavior.
For example:
#PostMapping(value = "/my-awesome-path")
public String myAwesomeMethod(
#RequestParam(name = "token", required = false) final String token,
#ModelAttribute("formData") final MyFormData formData) {
//class fields and members...
And, in the MyFormData you have this:
public class MyFormData{
private String token;
//other fields, getters and setters...
You will receive the query param token with the value duplicated and comma separated as well as in the MyFormData object.
The alternative is check the casuistic and change the parameter name or redesign if it's necessary.
In the example the query param name is changed like this:
#PostMapping(value = "/my-awesome-path")
public String myAwesomeMethod(
#RequestParam(name = "custom-token", required = false) final String customToken,
#ModelAttribute("formData") final MyFormData formData) {
//class fields and members...
And the values are not more duplicated nor comma separated.
Cheers.

Invoking service with #RequestParam and #RequestBody using postman client

Was trying to invoke the service
http://IP:8080/PQRS/LMN/XYZ/runTest/scheduledautomation/1/XYZ
with below JSON String
[ {"paramName":"TEST_TARGET_IDENTIFIER","paramValue":"ETest"},{"paramName":"TEST_SOURCE_ENTRY_IDENTIFIER","paramValue":"com.pack.etest"}]
#ResponseStatus(value = HttpStatus.NO_CONTENT)
#RequestMapping(value = "/runTest/scheduledautomation/{runId}/{testEngine}", method = RequestMethod.POST)
public void runScheduledAutomatedTest(#RequestParam String cronExpresssion,
#RequestParam(required = false) #DateTimeFormat(iso = ISO.DATE_TIME) LocalDateTime endTime,
#PathVariable Integer runId,
#PathVariable TestEngine testEngine,
#RequestBody List<TestEngineParam> testEngineParams) throws Exception { //Some Code }
Response :
Required String parameter 'cronExpresssion' is not present
how to invoke mixed #RequestParam and #RequestBody services on postman client ?
I fear you want a little bit too much: RequestParam, RequestBody AND the whole thing as a REST query. At least two of the three things are mutually exclusive.
I think you could even get Postmaster to do this by modifying the called URL to:
http://IP:8080/PQRS/LMN/XYZ/runTest/scheduledautomation/1/XYZ?cronExpression=your-expression
Of course this would ruin your REST interface, but as I said: your handler method is a little bit "over-ambitious".

How to handle a request with multiple parameters on Spring-MVC

I am sending following request that need to be handled by Spring-MVC but it does not.
http://localhost:2001/MyProject/flights/find?origin=LAX&destination=WA&departure=&arrival=&return=false
Code
#Controller
#RequestMapping("/flights")
public class FlightController {
#RequestMapping(value = "/find?origin={origin}&destination={destination}&departure={departure}&arrival={arrival}&return={return}", method = RequestMethod.GET)
public String findFlight(#PathVariable String origin,
String destination, Date departure, Date arrival, boolean return) {
That is not the correct way (or place) to use #PathVariable. You need to use #RequestParam.
#Controller
#RequestMapping("/flights")
public class FlightController {
#RequestMapping("/find")
public String findFlight(#RequestParam String origin
, #RequestParam String destination
, #RequestParam(required = false) Date departure
, #RequestParam(required = false) Date arrival
, #RequestParam(defaultValue = "false", required = false, value = "return") Boolean ret) { ... }
}
Note that return is a keyword in Java so you cannot use it as a method parameter name.
You will also have to add a java.beans.PropertyEditor for reading the dates because the dates will (presumably) be in a specific format.
Try this, may be it works:
#RequestMapping("/find")
public String findFlight(#RequestParam("origin") String origin
, #RequestParam("destination") String destination,....

Resources