Spring's Request Parameter retrieve value and validation - spring

I have a simple Spring Boot application which accepts various event types. I have put a validation on the controller to check against those Event types. If Event Type does not match the Enum from the calling Client App, i throw an exception. However if the Value matches one of the event i want to retrieve that value and do a custom logic from Controller to Service Class. Any idea on how it can be done. Here is the Rest Controller and Enum Class.
public enum EventTypes {
ADDONE("AddOne"),
DELETEONE("DeleteOne"),
ADDTWO("AddTwo"),
DELETETWO("DeleteTwo");
private String event;
private EventTypes(String event){
this.event = event;
}
}
Rest Controller:
#PostMapping(value = "/eventProcess", consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public GenericResponse produceEventMessage(
#Parameter(in = ParameterIn.QUERY, description = "", required = true)
#RequestParam(value = "eventType", required = true) EventTypes eventType,
#Valid #RequestBody MessageRequest messageEventRequest) {
LOG.info("Event Type ::::" + eventType); // I need to retrieve this value
... Remaining Business Logic to be executed
}
Client Application URL : http://localhost:8080/eventProcess?eventType=AddOne

Related

SpringBoot Define variables to use in the class

I have a SpringBoot project with two classes DashboardController.java and DashboardService.java. I need to define the variable that I get from the Controller to use it in the whole Service class. I explain the problem.
This is the method I have in the DashboardController.java, in which I collect by URL the variable iniDate from the front-end:
#GetMapping(path = { "/{employee_id}/{iniDate}" })
public EmployeeDashboardDto getEmployeeDashboarYearInidDto(
#ApiParam(value = "employee_id", required = true) #PathVariable("employee_id") Integer idEmployee,
#ApiParam(value = "iniDate", required = true) #PathVariable("iniDate") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate iniDate
) throws QOException {
return dashboardService.getEmployeeDashboardYearIniDto(idEmployee,iniDate);
}
And this is the method that I have in the DashboardService.java class in which I collect the iniDate variable from the Controller:
public EmployeeDashboardDto getEmployeeDashboardYearIniDto(Integer idEmployee, EmployeeDashboardDto iniDate) {
EmployeeDashboardDto initDate = iniDate;
return initDate;
}
I'm not sure if I collect the variable correctly, but what I need first is to collect the variable from the front-end in the controller using the URL, then collect it in the service and finally define that variable is the service to use it in the rest of the methods.
You are receiving the date in your controller and casting it as LocalDate then your method in the service class needs to receive the date in the same type LocalDate.
Change the parameter type to LocalDate like this:
public EmployeeDashboardDto getEmployeeDashboardYearIniDto(Integer idEmployee, LocalDate iniDate) {
EmployeeDashboardDto employeeDashboardDto = new EmployeeDashboardDto();
employeeDashboardDto.setIniDate(iniDate);
return employeeDashboardDto;
}
If you need to receive dates as PathVariables you can check this or this answer.
I have created the following method in the DashboardService.java I have changed the name of the initDate variable:
public EmployeeDashboardDto getEmployeeDashboardYearIniDto(Integer employeeId, LocalDate iniDate) {
EmployeeDashboardDto initDate = new EmployeeDashboardDto();
initDate.setIniDate(iniDate);
return initDate;
}
And in the Dto I have created the following method:
public void setIniDate(LocalDate iniDate) {
// TODO Auto-generated method stub
}
But in the Service, in the methods that is used the initDate variable says that initDate cannot be resolved to a variable

How to accept RequestBody of different class types dynamically

I am using Spring Boot . Writing rest api's
where for the same api url , the request json structure varies
Is there any way we can apply Factory design or some thing else
#RequestMapping(value = "/myservice/{type}", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<?> myServiceApi(#PathVariable String type,
#RequestBody SomeClass1 somereq) {
// here based on type , the RequestBody can be either SomeClass1 or SomeClass2
// both the SomeClass1 and SomeClass2 has nothing in common .
}
The above code will work only if the request json is in SomeClass1 format , but i needed it to accept among {SomeClass1 , SomeClass2}
You could do this by passing the JSON as a String into your controller method and then mapping this to whichever object you expect to need:
#PostMapping(value = "/myservice/{type}")
public ResponseEntity<?> myServiceApi(#PathVariable String type,
#RequestBody String somereq) {
ObjectMapper mapper = new ObjectMapper();
if (<something that indicates SomeClass1>) {
SomeClass1 someClass1 = mapper.readValue(somereq, SomeClass1.class);
} else if (<something that indicates SomeClass2>) {
SomeClass2 someClass2 = mapper.readValue(somereq, SomeClass2.class);
}
}
Although to be honest if you really are expecting bodies with completely different structures my advice would be to just make separate API calls for these.

Spring common method between #RequestMappings

In a Servlet, you can include an #Override service method which gets called before the doGet or doPost, is there a way to achieve the same in a Spring #Controller?
Or more precisely, in each method in the Controller, I need to make sure an Entity (in this case, a Product) exists and redirect otherwise, like so, so how would one achieve that in Spring? Note that I also need the Product available in each Method.
#Controller
#RequestMapping("/product/{prod_id}/attribute")
public class AttributeController {
#Autowired
private AttributeService attributeService;
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String add(Model model, #PathVariable Long prod_id) {
Product product = attributeService.getProduct(prod_id);
if (product == null) {
return "products/products";
}
model.addAttribute("product", product);
model.addAttribute("attribute", new Attribute());
return "products/attribute_add";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String save(Model model, #PathVariable Long prod_id, #Valid Attribute attribute, BindingResult result) {
Product product = attributeService.getProduct(prod_id);
if (product == null) {
return "products/products";
}
// ...
}
// ...
}
This can be done with HandlerInterceptor. All you need to do is to extend HandlerInterceptorAdapter#preHandle and then register your interceptor through WebMvcConfigurer#addInterceptors. You can choose to use interceptor with all your mappings or with some specific mappers through InterceptorRegistration object with is returned by InterceptorRegistry#addInterceptor method.
By the way, HandlerInterceptors are useful to do some utility operations with requests and responses in general, like logging, adding headers, authentication, etc. For business-related operations I would recommend to use ControllerAdvice with custom business-oriented exceptions. In this case it would be a method which retrieves Product from database and throws custom exception if not found.

#ModelAttribute in Controller does not auto increment ID

I am working on Spring web based project and I am having trouble with #ModelAttribute that return model object to JSP file to be filled then it will be passed to controller function then data will be saved to database. Let me show you some code.
It is my Software Engineering Course Project for more detailed information code is available on github:
https://github.com/IYTECENG316SoftwareEngineering/reddit
#Controller
public class MessageController {
#ModelAttribute("privateMessage")
public PrivateMessage constructPrivateMessage() {
return new PrivateMessage();
}
#RequestMapping(value = "/message/{id}", method = RequestMethod.POST, params = "sendMessage")
public String doSendMessage(Model model, #PathVariable("id") int id,
#Valid #ModelAttribute("privateMessage") PrivateMessage privateMessage, BindingResult result,Principal principal) {
if (result.hasErrors()) {
return showMessage(model,id);
}
User messageOwner = userService.findOne(principal.getName());
//I need to create new instance of PrivateMessage because
//(#ModelAttribute("privateMessage") PrivateMessage privateMessage) this gives always same ID.
PrivateMessage message = new PrivateMessage();
message.setMessage(privateMessage.getMessage());
message.setUser(messageOwner);
PrivateMessageConversation conversation = messageService.findOneWithMessages(id);
message.setPrivateMessageConversation(conversation);
messageService.save(message);
return "redirect:/message/"+message.getID()+".html";
}
}
PrivateMessage object send to jsp file and it filled send back to doSendMessage function with #ModelAttribute. Object come with filled (all the inputs written in to object perfectly) but only problem is that its ID is not auto-incremented. There is one more code that I want to show. We use same template for topic and it works perfectly.Here the code;
#Controller
public class UserController {
#ModelAttribute("topic")
public Topic contructTopic() {
return new Topic();
}
#ModelAttribute("entry")
public Entry contructEntry() {
return new Entry();
}
#RequestMapping(value = "/account", method = RequestMethod.POST)
public String doAddNewTopic(Model model,
#Valid #ModelAttribute("topic") Topic topic,
BindingResult resultTopic, Principal principal,
#Valid #ModelAttribute("entry") Entry entry,
BindingResult resultEntry,
#RequestParam("topic_category") String category_string) {
System.out.println(principal.getName() + " " + category_string + " "
+ topic.getTitle() + " " + entry.getDescription());
if (resultTopic.hasErrors()) {
return account(model, principal);
}
if (resultEntry.hasErrors()) {
return account(model, principal);
}
String name = principal.getName();
Category category = categoryService.findByName(category_string);
topic.setCategory(category);
topicService.save(topic);
entry.setTopic(topic);
entry.setPublishedDate(new LocalDateTime());
entryService.save(entry, name);
return "redirect:/topic/" + topic.getId() + ".html";
}
}
Above code work perfectly. Topic and entry object send to jsp, they filled and send back to controller and all their attributes fine and IDs are auto-incremented. We could not figure auto why first one is not working.
NOTE: We are using Hibernate, Spring Data JPA and Tiles
In your first controller (MessageController) you are constructing a new instance of PrivateMessage and saving that. The hibernate generated id will be changed there. Then you are doing a redirect with a path pattern (redirect:/message/{id}.html). The pattern will be expanded with the original id the method doSendMessage has been called with.
In your second (working) controller you are not creating a new instance of Topic, so after saving topic the hibernate assigned id is contained in your topic. After that you are also not using springs path expansion but constructing a path by hand using the new id.
Change your first controller from
return "redirect:/message/{id}.html";
to
return "redirect:/message/" + message.getId() + ".html";
or
return UriComponentsBuilder.fromUriString("redirect:/message/{id}.html")
.buildAndExpand(message.getId())
.encode()
.toUriString()

Spring #PathVariable integration test

I'm trying ot write an integration test for one of my methods, that handles a form submission.
The problem is that i use #PathVariable int id in the #RequestMapping of my controller.
I get the following error message when my test method reaches the .handle() part.
nested exception is java.lang.IllegalStateException: Could not find #PathVariable [id] in #RequestMapping
So the controller cant reach the id. I'm using request.setRequestURI("/url-" + s.getId()); But appareantly its not helping with setting up #PathVariable.
Is there any way to set up the #PathVariable of my Mock object?
Update:
Yeah, im useing MockHttpServletRequest and annotationMethodHandlerAdapter.handle in my test class.
The Controller:
#RequestMapping(method = RequestMethod.POST, params = {"edit" })
public String onSubmit(#ModelAttribute("sceneryEditForm") SceneryEditForm s,
#PathVariable int id, Model model) {
// some code that useing id
}
The test method:
#Test
public void testEditButton() throws Exception {
MyObject s = new MyObject();
request.setMethod("POST");
request.setRequestURI("/edit-" + s.getId());
request.setParameter("edit", "set");
final ModelAndView mav = new AnnotationMethodHandlerAdapter()
.handle(request, response, controller);
assertViewName(mav, "redirect:/view-" + s.getId());
}
The error is correct: there is no path variable id
You need to add the path expression with an placeholder id
#RequestMapping(value = "/something/{id}",
method = RequestMethod.POST,
params = {"edit" })
public String onSubmit(#ModelAttribute("sceneryEditForm") SceneryEditForm s,
#PathVariable int id, Model model) {
// some code that useing id
}

Resources