【问题标题】:How to dynamically disable specific API in spring?如何在春季动态禁用特定API?
【发布时间】:2019-11-08 20:49:34
【问题描述】:

我有一个标志DISABLE_FLAG,我想用它来控制不同控制器中的多个特定API。

@RestController
public final class Controller1 {
    @RequestMapping(value = "/foo1", method = RequestMethod.POST)
    public String foo1()
}
@RestController
public final class Controller2 {
    @RequestMapping(value = "/foo2", method = RequestMethod.POST)
    public String foo2()
}

我可以使用拦截器来处理所有的 url。有没有像注释一样的简单方法?

【问题讨论】:

    标签: java spring annotations


    【解决方案1】:

    你可以使用 AOP 来做类似的事情。

    创建您自己的注释...

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Maybe { }
    

    以及相应的方面...

    @Aspect
    public class MaybeAspect {
    
      @Pointcut("@annotation(com.example.Maybe)")
      public void callMeMaybe() {}
    
      @Around("callMeMaybe()")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // do your logic here..
        if(DISABLE_FOO) {
          // do nothing ? throw exception?
          // return null;
          throw new IllegalStateException();
        } else {
          // process the request normally
          return joinPoint.proceed();
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      我认为没有直接的方法可以禁用构造的请求映射,但我们可以在某些条件下通过多种方式禁用 API。

      这是通过 spring 配置文件或 JVM 属性禁用的 2 种方法。

      public class SampleController {
          @Autowired
          Environment env;
      
          @RequestMapping(value = "/foo", method = RequestMethod.POST)
          public String foo(HttpServletResponse response) {
              // Using profile
              if (env.acceptsProfiles("staging")) {
                  response.setStatus(404);
                  return "";
              }
      
              // Using JVM options
              if("true".equals(System.getProperty("DISABLE_FOO"))) {
                  response.setStatus(404);
                  return "";
              }
      
              return "";
          }
      }
      

      如果您正在考虑使用云配置的未来解决方案是最好的方法。 https://spring.io/guides/gs/centralized-configuration/

      使用条件组件

      这允许构建带有条件的bean,如果条件在启动时失败,则永远不会构建整个组件。将所有可选请求映射分组到新控制器并添加条件注释

      @Conditional(ConditionalController.class)
      public class SampleController {
          @Autowired
          Environment env;
      
          @RequestMapping(value = "/foo", method = RequestMethod.POST)
          public String foo(HttpServletResponse response) {
              return "";
          }
      
          public static class ConditionalController implements Condition {
      
              @Override
              public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
                  return context.getEnvironment().acceptsProfiles("staging"); // Or whatever condition
              }
      
          }
      }
      

      【讨论】:

      • 问题是我必须使用标志来控制多个API。那么使用拦截器似乎更容易。
      • @eriee 使用条件注释更新了另一种方法
      • 我也更新了我的问题。我无法将这些 API 组合在一起。
      【解决方案3】:

      您可以通过使用弹簧配置文件通过注释来解决这个问题。您定义了两个配置文件,一个用于启用标志,另一个用于禁用标志。您的示例如下所示:

      @Profile("DISABLED_FLAG")
      @RestController
      public final class Controller1 {
          @RequestMapping(value = "/foo1", method = RequestMethod.POST)
          public String foo1()
      }
      
      @Profile("ENABLED_FLAG")
      @RestController
      public final class Controller2 {
          @RequestMapping(value = "/foo2", method = RequestMethod.POST)
          public String foo2()
      }
      

      这里是这个特性的 spring 框架文档的链接:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html

      【讨论】:

        【解决方案4】:

        我是这样做的:

        @Retention(RUNTIME)
        @Target(ElementType.METHOD)
        public @interface DisableApiControl {
        }
        

        这个类是我的自定义声明。之后可以使用 AOP :

        对于 AbstractBaseServiceImpl :

        public abstract class AbstractBaseServiceImpl {
        
            private static boolean disableCheck = false;
        
            public void setDisableChecker(boolean checkParameter) {
                disableCheck = checkParameter;
            }
        
            public boolean getDisableChecker() {
                return disableCheck;
            }
        }
        

        注意:上述类已准备好提供动态结构。

        @Aspect
        @Component
        public class DisableApiControlAspect extends AbstractBaseServiceImpl {
        
            @Autowired
            private HttpServletResponse httpServletResponse;
        
            @Pointcut(" @annotation(disableMe)")
            protected void disabledMethods(DisableApiControl disableMe) {
        
                // comment line 
                
            }
        
            @Around("disabledMethods(disableMe)")
            public Object dontRun(ProceedingJoinPoint joinPoint, DisableApiControl disableMe) throws Throwable {
                if (getDisableChecker()) {
                    httpServletResponse.sendError(HttpStatus.NOT_FOUND.value(), "Not found");
                    return null;
                } else {
                    return joinPoint.proceed();
                }
            }
        }
        

        checker 参数此时添加了全局。当需要时将值设置为 true / false 时,其余部分会更容易。

        @GetMapping("/map")
        @DisableApiControl
        public List<?> stateMachineFindMap() {
            return new ArrayList<>;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-23
          • 2019-01-10
          相关资源
          最近更新 更多