Spring MVC, Thymeleaf & REST

14,529

Solution 1

My preferred approach here is to use inheritance:

@RequestMapping('/foobars')
abstract class FoobarBaseController {

    @RequestMapping
    abstract listAll()
}

@Controller
class FoobarHtmlController extends FoobarBaseController {
    @Override ModelAndView listAll() {
        new ModelAndView('foobars/foobarThymeleafTemplate', [foobars: foobarsList])
    }
}

@RestController
@RequestMapping('/foobars', produces = MediaType.APPLICATION_JSON_VALUE)
class FoobarJsonController extends FoobarBaseController {
    @Override Collection<Foobar> listAll() {
        foobarsList
    }
}

Alternatively, if there's significant work to be done checking inputs or the like, you can implement that in the BaseController and have an abstract listAllResponse(DomainObject foo) that then returns the appropriate ModelAndView (HTML) or DTO (JSON).

The one pitfall of this approach is that you can't override just part of the @RequestMapping, so you do have to repeat the class's part of the mapping when you specify the produces parameter, but you can inherit the method-level mappings with no problem.

Solution 2

Expose Crud

Use the @Controller to do the work of handling HTML page and using the Repository you can expose the entity via rest API for the basic actions like crud by using SPRING-DATA-REST to expose your entities. I think you are already doing it by looking at the code

 @RestResource(rel = "by-id") 
    Marker findMarkerById(String id);

Expose Business logic

If you want to expose any business logic, you have to create a Service layer and simply call it via your @Controller for the web page. and create another controller as @RestController for the web API interface.

You may note, you are not duplicating any code here, as the logic is written at the single point in the Service layer. But using different controllers for different purposes.

As you don't need all the logic in web page to get exposed to API, use of separate controller for REST may be a clean design implementation for your code if you can namespace your code as app.web and app.api.

Food for thought

Use a complete REST API implementation for web pages and android. Then use AngualarJS or backboneJs to do the client side implementation with HTML5. I think this is the future.

Share:
14,529
WARREN-R
Author by

WARREN-R

C Name: Warren, Richard G. C MailTo:[email protected] C C First Program: Time and Expense calculator C Language Used: Fortran C Computer Used: IBM 360 Mainframe, C Punch Key Terminal, and Punch Cards C C College: University of California, San Diego C Occupation: Professional Software Guy C First Java App: 1997 with "Visual Cafe" C Sun Java 2 Certified (y/n): Y C C Contract or Direct Hire with: DARPA, BBN, C SAIC, Reticular Systems, Applied Systems C Intelligence, Intelligent Systems C Engineering, Decision Sciences, Motorola, C Teknowledge Corp., HNC Corp., C Fair Isaac Co., Intuit C C Publications: Warren, et al. “Bi-Direction C Technology Transfer Between Government C Computer Generated Agents and Commercial C Entertainment,” Fifth Conference on C Computer Generated Forces and Behavioral C Representation, pp. 329 - 336. C C Smith, et al. "Information Collecting C And Decision Making via Tiered Information C Network Systems,"United States Patent C Application Publication, C Pub. No. US2008/0258880 A1, Oct. 23, 2008. C C Favorite Position: Doggie ... umm ... err ... C sorry .. I mean Software Architect! C C Favorite Quote: Pighter Filot! C C EOF

Updated on July 20, 2022

Comments

  • WARREN-R
    WARREN-R almost 2 years

    I currently have a project using a Spring controller and Thymeleaf to create a little browser app. The controller class is declared as

    @Controller public class MyController {
    

    Inside the controller I have a GET defined as

    @RequestMapping(value = "/foobars", method = RequestMethod.GET)
    String index(Model model, --- more params ---) {
        //call repository and get foobarsList
        //model.addAttribute("foobars", foobarsList);
              ...
        return "foobars/foobarThymeleafTemplate"
    }
    

    The call repository and get foobarList is a call to a MongoRepository defined as:

    public interface FoobarRepository extends MongoRepository< ... cut for brevity> {
    
        @RestResource(rel = "by-id") 
        Marker findMarkerById(String id);
    
        ... additional @RestResources cut for brevity ...
    }
    

    Again, the Browser App looks great. The GET calls the repository, populates the model with the list of foobars and Thymeleaf does it thing with that list.

    PROBLEM: Now I need to access that same data from an Android App and I would prefer to use REST and just consume JSON in the Android App. I want to keep Thymeleaf but will refactor the browser app if necessary.

    QUESTION: Is there a way to somehow use the same @Controller or will I have to maintain a second FoobarRestController using @RestController with /restFoobars endpoints? The second REST controller works for sure but it seems kind of sloppy ... poor design.

    Your thoughts and recommendations?

    Thanks again. -Rich