Spring Boot. Forward on RestController

Viewed 78

I am implementing a mechanism for replacing short links.

I need to forwarded request to another controller. I found examples how to do it in spring on models, but I don't understand how to do it in RestControllers

Example what i found (use models)

@Controller
public class ShrotLinkForwardController {

   @RequestMapping("/s/*")
   public String myMethod(HttpServletRequest request) {
       return "forward:/difmethod";
   }
}

Or maybe I'm looking in the wrong direction and I need to make a filter?

UPD. I don't know the final endpoint, it is calculated in the forwarded method. So, i cant autowired other controller

3 Answers

There are 2 ways to achieve what you want.

1. Call the method on the target controller directly.

Controllers are just normal Spring beans. You can get it via autowire.

@Controller
public class ShrotLinkForwardController {

  @Autowired
  OtherController otherController;

  @RequestMapping("/s/*")
  public String myMethod(Model model) {
    otherController.doStuff();
    return ...;
  }
}

2. Trigger the forward by returning a string

To trigger the forward, try returning a String instead of ModelAndView.

This is the approach you mentioned in your question. Note that the syntax should be forward:/forwardURL. The string after forward: is the URL pointing to another controller, not the method name.

@Controller
public class ShrotLinkForwardController {

  @RequestMapping("/s/*")
  public String myMethod(Model model) {
    return "forward:/forwardURL";
  }
}

you could inject the target controller and simply call the method

@Controller
public class ShortLinkForwardController {

   @Autowired
   private RestController target;

   @RequestMapping("/s/*")
   public String myMethod(HttpServletRequest request) {
       return target.myMethod(request);
   }
}

Caveat: Path related request properties will still point to "/s/*"

Or use ResponseEntity and set target location...

public ResponseEntity<Void> myMethod(HttpServletRequest request) {
  return ResponseEntity.status(302).location(URI.create(...)).build();  
}

UPD. I don't know the final endpoint, it is calculated in the forwarded method. So, i cant autowired other controller

but I don't understand how to do it in RestControllers

I can see some indications of possible bad design here, so I will try to explain the possible issues and how should be handled according to best practices.

If your requirement is to make a forward to another controller, then this might be an indication of 3 possible issues:

  1. The job to be done by the other controller (which you say you want to forward to) can be extracted into a service method in service layer. Then both controllers can call the same service method, without each controller be aware of the other.

  2. Your need could also be an indicator of the following issue. You need 2 controllers for exactly the same practical reason, so that they provide for same input exactly the same output, but to be available from 2 different URLs. If this is the case then you can use just 1 controller and allow it to be executed for both URLs. See the following code to achieve this:

    @RequestMapping({"/s/*", "/s2/*})
    public String myMethod(HttpServletRequest request) {
        return "some response";
    }
    
  3. You need to expose only 1 URL to the client which will serve everything. Then the approach with forward will also not benefit you, since the client will be able to reach the other forwarded controller directly if he wishes so. In this case you can implement 1 single controller which then according to the needs builds different responses. You can do this in RestController although not suggested by Sonar and other code review tools by marking the method to return ResponseEntity<?>. Example:

      @RequestMapping("/s/*")
      public ResponseEntity<?> myMethod(HttpServletRequest request) {
            if (condition 1) {
                return new ResponseEntity<YourObject1>(HttpStatus.OK);
            } else if (condition 2) {
                return new ResponseEntity<YourObject2>(HttpStatus.OK);
            } else {
                return new ResponseEntity<YourObject3>(HttpStatus.OK);
            }
     }
    

    this last choice is not considered best practice with <?> but for this requirement I don't see any other way out.

Related