如何使用 ModelAndView 在 RestController 上实现 REST API

Posted

技术标签:

【中文标题】如何使用 ModelAndView 在 RestController 上实现 REST API【英文标题】:How to implement REST API on a RestController using ModelAndView 【发布时间】:2019-10-22 10:57:56 【问题描述】:

当我的控制器中的方法返回 ModelAndView 时,我正在努力解决如何实现 API。我能找到的大多数教程都返回 ResponseEntities。我应该制作单独的 API 控制器来严格处理 /api 映射下的 API 调用吗? (我认为这不是 RESTFUL 做法)。或者是否可以在同一个控制器中处理我的 API 调用,即使使用 ModelAndView 也是如此?

我的控制器如下所示:

@RestController
@RequestMapping("/dish")
public class DishController 

    private final DishRepository dishRepository;

    public DishController(DishRepository dishRepository) 
        this.dishRepository = dishRepository;
    

    @GetMapping
    public ModelAndView list() 
        Iterable<Dish> dishes = this.dishRepository.findAll();
        return new ModelAndView("dishes/list", "dishes", dishes);
    

    @GetMapping("id")
    public ModelAndView view(@PathVariable("id") Dish dish) 
        return new ModelAndView("dishes/view", "dish", dish);
    

    @GetMapping(params = "form")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String createForm(@ModelAttribute Dish dish) 
        return "dishes/form";
    

    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public ModelAndView create(@Valid Dish dish, BindingResult result,
                               RedirectAttributes redirect) 
        if (result.hasErrors()) 
            return new ModelAndView("dishes/form", "formErrors", result.getAllErrors());
        
        dish = this.dishRepository.save(dish);
        redirect.addFlashAttribute("globalMessage", "view.success");
        return new ModelAndView("redirect:/d/dish.id", "dish.id", dish.getId());
    

    @RequestMapping("foo")
    public String foo() 
        throw new RuntimeException("Expected exception in controller");
    

    @ResponseStatus(HttpStatus.OK)
    @GetMapping("delete/id")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public ModelAndView delete(@PathVariable("id") Long id) 
        this.dishRepository.deleteById(id);
        Iterable<Dish> dishes = this.dishRepository.findAll();
        return new ModelAndView("dishes/list", "dishes", dishes);
    

    @ResponseStatus(HttpStatus.OK)
    @GetMapping("/modify/id")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public ModelAndView modifyForm(@PathVariable("id") Dish dish) 
        return new ModelAndView("dishes/form", "dish", dish);
    

【问题讨论】:

问题更多的是设计选择。 【参考方案1】:

@RestController 是写 @Controller@ResponseBody 的简写,只有在所有方法都返回一个应该被视为响应正文的对象(例如 JSON)时才应该使用它。

如果您想在同一个控制器中组合 REST 端点和 MVC 端点,您可以使用 @Controller 对其进行注释,并使用 @ResponseBody 单独注释每个方法。

例如:

@Controller // Use @Controller in stead of @RestController
@RequestMapping("/dish")
public class DishController 

    @GetMapping("/list")
    public ModelAndView list()  /* ... */ 

    @GetMapping
    @ResponseBody // Use @ResponseBody for REST API methods
    public List<Dish> findAll()  /* ... */ 

或者,正如您所提到的,您可以使用多个控制器:

@Controller
@RequestMapping("/dish")
public class DishViewController  /* ... */ 

@RestController
@RequestMapping("/api/dish")
public class DishAPIController  /* ... */ 

【讨论】:

非常感谢您解决了很多问题!我想我会选择多个控制器 @Bram 听起来是个好主意。我通常还会将我的 API 控制器映射与我的 API 控制器映射分开。【参考方案2】:

你不应该在 RestController 中使用 Model 和 View。 RestController 的主要目标是返回数据,而不是视图。在这里查看更多详细信息:Returning view from Spring MVC @RestController。

【讨论】:

以上是关于如何使用 ModelAndView 在 RestController 上实现 REST API的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ModelAndView 中使用重定向

如何使用modelandview进行重定向

什么时候在 Spring 中使用 ModelAndView 和 Model?

JAVA 如何获取modelandview的返回值,如果把一个对象放在map集合里(假如集合名字是

如何在 spring 控制器部分更改 modelandview 的视图内容?

ModelAndView的介绍