如何全局传递模型属性以避免在Spring控制器中重复编写代码?

huangapple go评论77阅读模式
英文:

How to pass model attributes globally to avoid repeatable code in Spring controller?

问题

以下是代码中需要翻译的部分:

Repeatable piece of code

model.addAttribute("hotels", hotelService.getAllHotels());
List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
model.addAttribute("rooms", roomsDto);
model.addAttribute("roomTypes", roomTypeService.findAllRoomTypeNames());

Full method with that piece of code appearing twice

@PostMapping("/hotels/{hotelId}/rooms")
    public String createRoomForHotelById(@ModelAttribute("room") @Valid NewRoomDto roomDto,
                                         BindingResult result,
                                         @PathVariable("hotelId") Long hotelId,
                                         Model model) {
        if(result.hasErrors()) {
            // SAME CODE
            model.addAttribute("hotels", hotelService.getAllHotels());
            List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute("rooms", roomsDto);
            model.addAttribute("roomTypes", roomTypeService.findAllRoomTypeNames());
            //
            
            model.addAttribute("hotel", new NewHotelDto());
            LOG.info("Binding error: {}", result.toString());
            return "admin/dashboard";
        }
        
        // SAME CODE
        model.addAttribute("hotels", hotelService.getAllHotels());
        List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
        model.addAttribute("rooms", roomsDto);
        model.addAttribute("roomTypes", roomTypeService.findAllRoomTypeNames());
        //
        
        LOG.info("AdminController: CreateRoomForHotelById: Created room: {}", roomDto.toString());
        
        roomDto.setHotelId(hotelId);
        roomService.createNewRoom(roomDto);
        
        return "redirect:/auth/admin/hotels/{hotelId}/rooms";
    }
英文:

I would like to ask you for some best practice to reduce the amount of repeatable code in my controller's methods - one of them is presented below.

I have quite a big and complex view with a lot of information and two forms. In every method of my controller (and there are quite a few) I have to pass the same attributes to my view (in post controllers even twice). I added a note SAME CODE in the code snippet below indicating identical pieces of code.

I wonder if there is any possibility to make a one global method in the controller gathering all attributes to be passed to the model and just reference it in any of particular methods?

I looked into ModelAndView or ModelMap, but cannot see those being suitable here.

Just want to avoid repeating this part:

Repeatable piece of code

model.addAttribute(&quot;hotels&quot;, hotelService.getAllHotels());
List&lt;GetRoomDto&gt; roomsDto = roomService.getAllRoomsByHotelId(hotelId);
model.addAttribute(&quot;rooms&quot;, roomsDto);
model.addAttribute(&quot;roomTypes&quot;, roomTypeService.findAllRoomTypeNames());

Full method with that piece of code appearing twice

@PostMapping(&quot;/hotels/{hotelId}/rooms&quot;)
    public String createRoomForHotelById(@ModelAttribute(&quot;room&quot;) @Valid NewRoomDto roomDto,
                                         BindingResult result,
                                         @PathVariable(&quot;hotelId&quot;) Long hotelId,
                                         Model model) {
        if(result.hasErrors()) {
            // SAME CODE
            model.addAttribute(&quot;hotels&quot;, hotelService.getAllHotels());
            List&lt;GetRoomDto&gt; roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute(&quot;rooms&quot;, roomsDto);
            model.addAttribute(&quot;roomTypes&quot;, roomTypeService.findAllRoomTypeNames());
            //
            
            model.addAttribute(&quot;hotel&quot;, new NewHotelDto());
            LOG.info(&quot;Binding error: {}&quot;, result.toString());
            return &quot;admin/dashboard&quot;;
        }
        
        // SAME CODE
        model.addAttribute(&quot;hotels&quot;, hotelService.getAllHotels());
        List&lt;GetRoomDto&gt; roomsDto = roomService.getAllRoomsByHotelId(hotelId);
        model.addAttribute(&quot;rooms&quot;, roomsDto);
        model.addAttribute(&quot;roomTypes&quot;, roomTypeService.findAllRoomTypeNames());
        //
        
        LOG.info(&quot;AdminController: CreateRoomForHotelById: Created room: {}&quot;, roomDto.toString());
        
        roomDto.setHotelId(hotelId);
        roomService.createNewRoom(roomDto);
        
        return &quot;redirect:/auth/admin/hotels/{hotelId}/rooms&quot;;
    }

答案1

得分: 1

对于全局模型属性,您可以使用 @ControllerAdvice:

创建一个类,并用 @ControllerAdvice 进行注解。
在该类中,像这样传递模型属性(现在将全局可用):

@ModelAttribute("foo")
public Foo foo() {
    return new Foo();
}
英文:

For global model attributes you can use @ControllerAdvice:

Create a class and annotate it with @ControllerAdvice.
Inside of that class pass the model attribute (which will now be available globally) like so:

@ModelAttribute(&quot;foo&quot;)
public Foo foo() {
    return new Foo();
}

答案2

得分: 1

你也可以将代码从J Asgarov的回答移到同一个控制器中,而不是放在另一个使用@ControllerAdvice注解的类中。这样,该代码只会在该控制器内的@RequestMapping方法中执行。

对于多个值,你也可以像这样做:

@ModelAttribute
public void foo(Model model, @PathVariable(required = false) Long hotelId) {
        model.addAttribute("hotels", hotelService.getAllHotels());
        if (hotelId != null) {
            List<GetRoomDto> roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute("rooms", roomsDto);
        }
        model.addAttribute("roomTypes", roomTypeService.findAllRoomTypeNames());
}

但根据你的代码,我更建议将重复的代码移到一个私有方法中,每当需要时调用它,比如你的createRoomForHotelById方法会导致重定向,基本上会丢弃你放在模型中的所有内容。

英文:

You can also move the code from J Asgarov's answer into the same controller instead of another class annotated with @ControllerAdvice. That way that code will only be executed for @RequestMapping methods within that controller.

For multiple values you could also do something like this:

@ModelAttribute
public void foo(Model model, @PathVariable(required = false) Long hotelId) {
        model.addAttribute(&quot;hotels&quot;, hotelService.getAllHotels());
        if (hotelId != null) {
            List&lt;GetRoomDto&gt; roomsDto = roomService.getAllRoomsByHotelId(hotelId);
            model.addAttribute(&quot;rooms&quot;, roomsDto);
        }
        model.addAttribute(&quot;roomTypes&quot;, roomTypeService.findAllRoomTypeNames());
}

But seeing your code I would rather suggest you move the repeated code into a private method and call it whenever you need those inside your model.
Your method createRoomForHotelById for example causes a redirect, which basically discards everything you put in your model.

huangapple
  • 本文由 发表于 2020年8月14日 02:40:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63401345.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定