gpt4 book ai didi

rest - 如何选择性地停用 Java 应用程序中的 REST 端点?

转载 作者:行者123 更新时间:2023-12-04 04:04:12 26 4
gpt4 key购买 nike

我正在开发一个由多个后端服务和一个前端客户端组成的应用程序。整个应用程序是用 Java 编写的,我们使用 Apache TomEE 网络服务器来运行它。

后端服务公开了几个 API,并包含多个 Controller 。其中一些 API 可供前端客户端访问,一些用于后端服务之间的内部通信。

日志记录对于此应用程序非常重要。需要在开始正常操作之前始终初始化日志记录系统(以确保完全可追溯性)。该应用程序使用一个安全的日志系统,该系统需要一个 key 来初始化日志(日志是使用这个 key 签名的,以防止日志被篡改)。还需要将日志记录 key 上传到每个服务。每个后端服务都有一个用于接收日志记录 key 的端点。

存在“鸡或蛋”类型的问题。应用程序需要运行才能接收 key ,但在接收到 key 之前,应用程序不应完全运行。

为了满足要求,我们正在考虑以下启动程序:

  • 以简化的操作模式启动后端服务,其中每个服务中唯一可访问的端点是用于接收传入 key 的端点。
  • 一旦接收到 key ,并且日志系统初始化,然后激活其他端点,并开始正常操作。

  • 是否有激活端点以促进此启动过程的标准方法?或无论如何控制对端点的访问。

    一些额外信息:应用程序中的 Controller 类不扩展任何其他类,并且仅用 @Path 修饰。和 @Stateless注释。

    更新 1

    我遵循了使用过滤器的方法(如下面的 Bogdan 建议的那样)。我创建了一个过滤器来捕获所有请求。应用程序正确启动。 init()过滤器类中的方法被调用。但是当我访问 /installkey端点发生错误。

    似乎发生的是 doFilter(ServletRequest, ServletResponse, FilterChain)方法被调用,我的代码检测到请求是针对 /installkey端点。但是一个错误来自调用: filterChain.doFilter(request, response); .

    我查过了,我知道变量 filterChain不是 null , 但是在方法 doFilter(ServletRequest, ServletResponse, FilterChain) 中出了点问题,我无法调试它。

    可能是我没有初始化需要初始化的东西。

    我已经添加了我在下面得到的输出。

    现在我的 web.xml 中有以下内容:

    <filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.company.filter.LoggingFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>*</url-pattern>
    </filter-mapping>

    以及以下类(class):

    public class LoggingFilter implements Filter {

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(final ServletRequest request,
    final ServletResponse response,
    final FilterChain filterChain) throws IOException,
    ServletException {

    String url = "";
    if (request instanceof HttpServletRequest) {
    url = ((HttpServletRequest) request).getRequestURL().toString();
    }

    if (url.endsWith("/installkey/")) {
    filterChain.doFilter(request, response);
    return;
    } else if (loggerConfig.isInitialized()) {
    filterChain.doFilter(request, response);
    return;
    }
    }

    public void destroy() {
    System.out.println("XXXXXXXXXXX Running destroy");
    }
    }

    但我收到以下错误:

    Jan 19, 2016 10:42:25 AM org.apache.catalina.core.StandardWrapperValve invoke
    SEVERE: Servlet.service() for servlet [default] in context with path [/vw-ws-rest] threw exception [Error processing webservice request] with root cause
    java.lang.NullPointerException
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:240)
    at org.apache.openejb.server.cxf.rs.CxfRsHttpListener.doInvoke(CxfRsHttpListener.java:227)
    at org.apache.tomee.webservices.CXFJAXRSFilter.doFilter(CXFJAXRSFilter.java:94)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.company.filter.LoggingFilter.doFilter(LoggingFilter.java:63)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

    更新 2

    作为替代方案,我尝试了使用 JAX-RS 名称绑定(bind)的方法,正如 Cássio Mazzochi Molin 所建议的那样。

    我创建了界面:
    import javax.ws.rs.NameBinding;

    @NameBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD })
    public @interface TemporarilyDisabled {
    }

    我创建了一个过滤器类,如下所示:
    @Provider
    @TemporarilyDisabled
    public class LoggingFilter implements ContainerRequestFilter {

    @Override
    public void filter(final ContainerRequestContext requestContext) throws IOException {

    System.out.println("in filter method!");
    }
    }

    并更新了我的资源 Controller 类如下:
    @Path("installkey")
    @Stateless(name = "vw-installKeyResource")
    public class VwInstallKeyResource {

    @Inject
    private Logger LOG;

    @EJB
    //... some required classes

    @POST
    @TemporarilyDisabled
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response savePlatformData(final InstallKeyData installKeyData)
    throws CryptographicOperationException, DuplicateEntryException {

    ....
    }
    }

    此应用程序正在使用我无法更新的 Java EE 6。为了测试这种方法,我必须向应用程序添加以下依赖项:
    <!-- JAX-RS -->
    <dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0</version>
    </dependency>

    代码全部编译正常,应用程序启动正常。

    但是当我访问端点(应该被过滤器捕获的端点)时,过滤器代码不会被执行(我从未在过滤器方法中看到打印语句),并且端点只是正常执行。

    由于某种原因,过滤器没有捕获请求。

    我不知道问题是否与端点是 POST 的事实有关。或者,可能 JAX-RS 没有找到过滤器类,它用@provider 装饰,但我不知道是否需要以任何其他方式注册过滤器。

    最佳答案

    我认为您不会找到任何开箱即用的解决方案。
    使用 JAX-RS 2.0 时及其实现,您会发现一些很棒的资源:您可以使用名称绑定(bind)过滤器根据您的条件中止对某个端点的请求。
    这种方法最酷的地方在于,您可以保持端点精简并专注于其业务逻辑。负责中止请求的逻辑将在过滤器中。要暂时禁用一个或多个端点,您只需在它们上放置一个注释。它将激活阻止请求到达端点的过滤器。默认情况下启用所有端点,您将有选择地禁用您不想接收请求的端点。
    定义名称绑定(bind)注解
    要将过滤器绑定(bind)到您的 REST 端点,JAX-RS 2.0 提供了元注释 @NameBinding .它可用于创建其他注释,这些注释将用于将过滤器绑定(bind)到您的 JAX-RS 端点。
    考虑 @TemporarilyDisabled注释定义如下。注释为 @NameBinding :

    @NameBinding
    @Retention(RUNTIME)
    @Target({TYPE, METHOD})
    public @interface TemporarilyDisabled { }
    阻止 HTTP 请求到达您的端点 @TemporarilyDisabled上面创建的注解将用于装饰一个过滤器类,它实现了 ContainerRequestFilter ,允许您中止请求:
    @Provider
    @TemporarilyDisabled
    public class TemporarilyDisableEndpointRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

    if (isEndpointTemporarilyDisabled) {
    requestContext.abortWith(Response.status(Response.Status.SERVICE_UNAVAILABLE)
    .entity("Service temporarily unavailable.")
    .build());
    }
    }
    }
    @Provider 注释标记了扩展接口(interface)的实现,在提供者扫描阶段应该可以被 JAX-RS 运行时发现。
    您可以编写任何条件来测试您的端点是否应该暂时禁用。
    在上面的例子中:
  • 如果 isEndpointTemporarilyDisabled条件被评估为 true ,请求将被 HTTP 503 Service Unavailable 中止。回复。
  • 如果 isEndpointTemporarilyDisabled评估为 false ,请求不会被中止,并将到达用户请求的端点。

  • 为什么使用 503 Service Unavailable 中止请求回复?
    根据 RFC 2616 , HTTP 503 Service Unavailable应在以下情况下使用:

    10.5.4 503 Service Unavailable

    The server is currently unable to handle the request due to atemporary overloading or maintenance of the server. The implicationis that this is a temporary condition which will be alleviated aftersome delay. If known, the length of the delay MAY be indicated in aRetry-After header. If no Retry-After is given, the client SHOULDhandle the response as it would for a 500 response.

    Note: The existence of the 503 status code does not imply that aserver must use it when becoming overloaded. Some servers may wishto simply refuse the connection.


    将过滤器绑定(bind)到您的端点
    要将过滤器绑定(bind)到您的端点方法或类,请使用 @TemporarilyDisabled 注释它们上面定义的注释。对于被注释的方法和/或类,过滤器将被执行:
    @Path("/")
    public class MyEndpoint {

    @GET
    @Path("{id}")
    @Produces("application/json")
    public Response myMethod(@PathParam("id") Long id) {
    // This method is not annotated with @TemporarilyDisabled
    // The request filter won't be executed when invoking this method
    ...
    }

    @DELETE
    @Path("{id}")
    @TemporarilyDisabled
    @Produces("application/json")
    public Response myTemporarilyDisabledMethod(@PathParam("id") Long id) {
    // This method is annotated with @TemporarilyDisabled
    // The request filter will be executed when invoking this method
    ...
    }
    }
    在上面的例子中,请求过滤器将只对 myTemporarilyDisabledMethod(Long) 执行。方法,因为它带有 @TemporarilyDisabled 注释.

    关于rest - 如何选择性地停用 Java 应用程序中的 REST 端点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34840144/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com