MVC - break controller to subclass hierarchy - spring

I currently have a single controller (implemented using Spring mvc), for students and teachers to upload files Controller name is FileUpoadController
I would like to break the controller functionality and extend it using:
StudentFileUploadController extends FileUpoadController
LecturerFileUpoadController extends FileUpoadController
So FileUpoadController will be abstract and maintain the base functionality.
Current controller:
#Controller
#RequestMapping(value = "/upload")
public class FileUpoadController
One solution is to make different upload mapping with two controllers
#RequestMapping(value = "/uploadStudent")
#RequestMapping(value = "/uploadLecturer")
Is it possible any other way?

It is very unlikely that those method behave exactly the same besides the different mapping. But you can use delegation instead:
public class FileUpoadController<T> {
public List<T> getList(){
// returns list of T
}
}
#Controller(value = "/uploadStudent")
public class UploadStudentController extends FileUpoadController<UploadStudent>{
#RequestMapping(method = RequestMethod.GET, value = "/list")
public #ResponseBody List<UploadStudent> getStudent() {
return super.getList();
}
}
#Controller(value = "/uploadLecturer")
public class UploadLecturerController extends FileUpoadController<UploadLecture>{
#RequestMapping(method = RequestMethod.GET, value = "/list")
public #ResponseBody List<UploadLecture> getLecture() {
return super.getList();
}
}
for more details please refer this:
https://www.codeproject.com/Articles/799677/The-Hierarchy-of-Controller-Class-in-ASP-NET-MVC

Related

spring boot 3: Async Controller doesn't work when inherent async methods from interface

Using OpenApi to generate interfaces with "async : 'true'" to have asynchronous controller method handling, I tried to implement those interfaces but I get always 404. I checked also the '/actuator/mappings' method to see whether my endpoints but they don't appear in that list of mappings.
I tried also to have async method without inheriting from an interface and the mapping was correctly considered.Below are some examples:
Async from interface
public interface TestApi {
#GetMapping(value = "/greeting-asnyc-api/{name}", produces = {"application/json", "application/xml"})
CompletableFuture<ResponseEntity<String>> greetingAsync(#PathVariable("name") final String name);
}
// This example doesn't work and returns 404
#RestController
public class TestApiController implements TestApi {
#Async
#Override
public CompletableFuture<ResponseEntity<String>> greetingAsync(final String name) {
return CompletableFuture.completedFuture(ResponseEntity.ok("Greeting " + name));
}
}
// This example works fine
#RestController
public class TestController {
#Async
#GetMapping(value = "/greeting/{name}", produces = {"application/json"})
public CompletableFuture<ResponseEntity<String>> greeting(#PathVariable("name") final String name) {
return CompletableFuture.completedFuture(ResponseEntity.ok("Greeting " + name));
}
}
The first example, i.e., TestApiController, doesn't work unfortunately and returns always 404 (also not found on '/actuator/mappings'), unlike the second example which works fine without any issues (can also be found on '/actuator/mappings')
Sync from interface
public interface TestApi {
#GetMapping(value = "/greeting-sync/{name}", produces = {"application/json", "application/xml"})
ResponseEntity<String> greeting(#PathVariable("name") final String name);
}
#RestController
public class TestApiController implements TestApi {
#Override
public ResponseEntity<String> greeting(final String name) {
return ResponseEntity.ok("Greeting " + name);
}
}
The example above also works fine when inheriting synchronous method from interface.
Did anyone face similar issue? Thanks for helping

How to specify the controller name if two controllers have same RequestMapping path value?

There are 2 controllers having same #RequestMapping value :
package com.ambre.hib.controller;
#Controller
public class AppointmentsController {
#RequestMapping(value = "/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
}
package com.ambre.hib.controller;
#Controller
public class ClientsController {
#RequestMapping(value = "/new", method = RequestMethod.GET)
public ClientForm getNewForm() {
return new ClientForm();
}
}
So the 2 controllers have same "/new" action.
Now from a jsp page I want to target a link to the "/new" action of the second controller : <img src="resources/images/plus.png" />
This writing is ambiguous because Spring does not know into which controller to look ! So how to specify the controller name in the link target ?
It's not possible to have two or more controller methods with the same #RequestMapping. The dispatcher won't know wich method to call.
You could set a base request mapping for each controller:
package com.ambre.hib.controller;
#Controller
#RequestMapping("/appointments")
public class AppointmentsController {
#RequestMapping(value = "/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
}
package com.ambre.hib.controller;
#Controller
#RequestMapping("/clients")
public class ClientsController {
#RequestMapping(value = "/new", method = RequestMethod.GET)
public ClientForm getNewForm() {
return new ClientForm();
}
}
If so, the way of calling each would be <a href="<c:url value='/appointments/new' />"> for the first controller and
<a href="<c:url value='/clients/new' />"> for the second
You need to narrow down the request using the "params" option. For example
#Controller
public class HelloWorldController {
#RequestMapping(value="/fetch", params = {"id=100"})
public String getInfo1(#RequestParam("id") String id) {
System.out.println("Inside getInfo1");
return "success";
}
#RequestMapping(value="/fetch", params = {"id=200"})
public String getInfo2(#RequestParam("id") String id) {
System.out.println("Inside getInfo2");
return "success";
}
}
When you access the URL /fetch?id=100 , method getInfo1() is called. When you access URL /fetch?id=200 , method getInfo2() is called and when you access /fetch?id=300 , HTTP Status 404 is received. In this case the "id" parameter is just another parameter which you use to narrow down the request to your preferred method in the controller.

How to create Exception with HttpStatus and send it using Resource of Spring-Hateoas?

I am using Spring-Boot 1.2.7 for developing Spring-Hateoas application with Spring-Data-JPA.
I have developed controller class with methods which returns Resource.
I want to create Exception with HttpStatus and use it in controller class for GET, POST, PUT and DELETE. Please assist me, I am new to this.
Controller Class - ArticleController
#RestController
#RequestMapping(value = "/api/articles")
public class ArticleController {
#Autowired
private ArticleService articleService;
#Autowired
private ArticleRepository articleRepository;
#Autowired
private ArticleResourceAssembler articleResourceAssembler;
/*#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<Resource<Article>> getArticles() {
Collection<Article> articles = articleService.findAll();
List<Resource<Article>> resources = new ArrayList<Resource<Article>>();
for (Article article : articles) {
resources.add(getArticleResource(article));
}
return resources;
}*/
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public PagedResources<Article> getArticles(Pageable pageable, PagedResourcesAssembler assembler) {
Page<Article> articles = articleService.findAll(pageable);
return assembler.toResource(articles, articleResourceAssembler);
}
#RequestMapping(value = "/{article_id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Resource<Article> getArticle(#PathVariable(value = "article_id") long article_id) {
Article article = articleService.findOne(article_id);
if (article == null) {
ResponseEntity.status(HttpStatus.NOT_FOUND);
}
return getArticleResource(article);
}
// Insert Article
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Article> createArtilce(#RequestBody Article article) {
article.setCreated(new Date());
Article savedArticle = articleService.create(article);
article.add(linkTo(methodOn(ArticleController.class).getArticle(savedArticle.getArticle_id()))
.withSelfRel());
// I want to return here HttpStatus.NOT_FOUND
}
private Resource<Article> getArticleResource(Article article) {
Resource<Article> resource = new Resource<Article>(article);
// Link to Article
resource.add(linkTo(methodOn(ArticleController.class).getArticle(article.getArticle_id())).withSelfRel());
return resource;
}
}
You need to implement a class for the exception (that is extends from RuntimeException) and annotated with #ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) or another Status code, and where you want throw that status, you need throw your CustomException.
For Exceptions you didn't write, can use a exception handler method inside your controller like this:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({ExternalException.class})
public void metodoCuandoExcepcionEsLanzada(){
//logging and processing
}

Spring MVC Using #RequestMapping on controller class with variable

I would like to write whole controller to work with entity.
I would like to declare the id of entity on class level and use it on each method. Here is the controller class:
#Controller
#RequestMapping(value="/job/{j_id}/instance")
public class JobController extends GenericController {
private final String htmlDir = "job/";
#RequestMapping(value="{i_id}/open", method=RequestMethod.GET)
public ModelAndView open(#PathVariable Long instance_id) {
ModelAndView result = new ModelAndView(htmlDir + "instance");
result.addObject("instance_id", instance_id);
Here I would like to use variable j_id from #RequestMapping
return result;
}
}
Can I achive this? Please help. Give me some code snippest please.
Have a try like this
#Controller
#RequestMapping(value="/job/{j_id}/instance")
public class JobController {
private final String htmlDir = "job/";
#RequestMapping(value="{i_id}/open", method=RequestMethod.GET)
public ModelAndView open(#PathVariable(value="j_id") Long instance_id) {
ModelAndView result = new ModelAndView(htmlDir + "instance");
result.addObject("instance_id", instance_id);
System.out.println("Instance Id -------------> " + instance_id);
return result;
}
}
Please notic "#PathVariable(value="j_id")"
To get both variables, you can change that line as following:
#RequestMapping(value="{i_id}/open", method=RequestMethod.GET)
public ModelAndView open(#PathVariable(value="j_id") Long jnstance_id, #PathVariable(value="i_id") Long instance_id) {
.....
}

spring controller does not work when I extend it

i try make a generic controller with some methods, so i don´t need re-writer common codes, but don´t work, why???
#Controller("/home/teste")
public class CtrlTeste extends ControladorGenericoSpring<Assistenciado>
{
public String path;
public CtrlTeste()
{
super(Assistenciado.class);
path = "/home/teste";
setPacoteServico("servico.assistenciado");
setPrefixo("Serv");
setNomeEntidade("Assistênciado");
}
#RequestMapping
public String teste(#RequestParam(value = "id", required= true)Long id, Model model)
{
Assistenciado ass = getServico().buscarPorId(id);
model.addAttribute("assistenciado", ass);
return "/home";
}
}
You're currently specifying the name of the bean. Try with:
#Controller
#RequestMapping("/home/teste")
public class CtrlTeste extends ControladorGenericoSpring<Assistenciado> {
...
}

Resources