【问题标题】:How to simplify REST controllers with same methods and different headers?如何简化具有相同方法和不同标头的 REST 控制器?
【发布时间】:2020-01-31 08:39:28
【问题描述】:

我有一个 Java REST API,供 2 个不同的消费者使用。默认情况下 REST 原则我的 API 应该定义请求标头的名称。但是现在我没有常见的情况。消费者使用不同的安全层提供不同的标头,这意味着两种方式的参数相同。

示例方法: (不是真实代码)
对于第一个消费者:

@PostMapping("/number")
Integer getNumber(@RequestHeader("no") String number, @RequestBody User user) {
    /*...*/
}

对于第二个消费者:

@PostMapping("/number")
Integer getNumber(@RequestHeader("number") String number, @RequestBody User user) {
    /*...*/
}

我在一个控制器中最多有 10 个方法,它们应该具有相同的名称和逻辑,但标题不同。请求路径前缀可能不同。


问题:

如何简化 REST 控制器,不创建 2 个具有相同方法和相同逻辑的不同控制器?


我尝试了什么:

我尝试了几个示例来创建一个具有 2 个不同接口的控制器,这些接口具有相同的方法,但映射不同。

示例:

控制器类

@RestController
@RequestMapping(path ="/application")
@Api(tags = {"application"})
public class ApplicationController implements AppMapping1, AppMapping2 {

    @Override
    public Integer getNumber(String number, User user) {
        /*...*/
    }
}

第一个界面

interface AppMapping1 {

    @PostMapping("/num")
    Integer getNumber(@RequestHeader("num") String number, @RequestBody User user);
}

第二界面

interface AppMapping2 {

    @PostMapping("/number")
    Integer getNumber(@RequestHeader("number") String number, @RequestBody User user);
}

结果:

控制器只映射第一个接口。所以http://.../application/num 工作正常,但http://.../application/number - 得到404 错误代码。这意味着 Java Spring-Boot 没有这样的功能。需要更多的想法。

使用Java 8开发的项目; spring-boot:2.1.1.RELEASE; gradle

【问题讨论】:

    标签: java spring rest spring-boot gradle


    【解决方案1】:

    根据this ,如果我们不确定哪些标头会出现,或者我们需要更多 它们比我们在方法签名中想要的要多,我们可以使用 没有特定名称的@RequestHeader 注解。

    变量类型有几种选择:MapMultiValueMapHttpHeaders 对象。

    样本

    @PostMapping("/number")
    public Integer getNumber(@RequestHeader Map<String, String> headers) {
    
        if (Optional.ofNullable(headers.get("no")).isPresent()){
            //...
        }
        else if (Optional.ofNullable(headers.get("number")).isPresent())
        {
            //...
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      为了接收具有不同名称的相同输入(numberno)而重复相同的代码块两次或更多次,对维护不友好。相反,建议读取所有标题并遍历它以使用不同的名称获取输入。

      示例代码

      @PostMapping("/number")
      public Integer getNumber(@RequestHeader Map<String, String> headers) {
          String number = headers.containsKey("number") ? headers.get("number") : headers.get("no");
          if(Objects.isNull(number)) {
              throw new RuntimeException("Number input not received from header!");
          }
      
          // relevant processing
      }
      

      【讨论】:

        【解决方案3】:

        我在https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-request-header.html找到了这个答案

        使用@RequestMapping(headers = ....) 避免歧义

        我们可以修复类似于我们使用的@RequestParam 的歧义 '参数' 。在@RequestHeader 的情况下,我们可以在 @RequestMapping 注解。

        @Controller
        @RequestMapping("trades")
        public class TradesController {
        
           @RequestMapping(headers = "User-Agent")
            public String handleAllTradesRequests (@RequestHeader("User-Agent") String userAgent,
                                                   Model model) {
                model.addAttribute("msg", "all trades requests, User-Agent header  : "
                                                                            + userAgent);
                return "my-page";
            }
        
            @RequestMapping(headers = "From")
            public String handleRequestByFromHeader (@RequestHeader("From") String from,
                                                     Model model) {
                model.addAttribute("msg", "trade request by From header  : " + from);
                return "my-page";
            }
        

        【讨论】:

        • 感谢您的回答。这不是处理不同名称的相同标题的答案
        【解决方案4】:

        您可以删除@RequestHeader 注释并考虑执行以下操作:

            @PostMapping("/number")
            Integer getNumber(HttpServletRequest request, @RequestBody User user) {
              String number = request.getHeader("num");
              if(number == null){
                 number = request.getHeader("number");
              }    
              /*...*/
            }
        

        如果您想要更简洁的方法,请考虑创建一个 util 类,该类接受 HttpServletRequest 对象并返回所需的标头值。

        【讨论】:

          【解决方案5】:

          最好的方法是将HttpServletRequest 添加为单个控制器的参数,并使用HttpServletRequest 对象提供的标头映射做一些逻辑。

          如果您想查看完整示例,请查看 here。我已经实现了我的单个控制器,它将我的所有逻辑相应地包装到标题/方法等。您可以使用HttpServletRequest 自定义逻辑。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-06-14
            • 1970-01-01
            • 2015-11-27
            • 2017-04-15
            • 2017-03-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多