列出 RestEasy 服务的所有公开/可用端点?

Posted

技术标签:

【中文标题】列出 RestEasy 服务的所有公开/可用端点?【英文标题】:List all exposed/available endpoints of RestEasy service? 【发布时间】:2013-11-16 16:55:02 【问题描述】:

是否可以以简单的方式

【问题讨论】:

【参考方案1】:

编辑:

有关“更清洁”的示例,请参阅此要点: https://gist.github.com/wonderb0lt/10731371


是的,这是可能的。也许你想知道怎么做? :)

这里有一个“快速-n-脏”的例子:

import org.jboss.resteasy.annotations.providers.jaxb.Formatted;
import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.ResourceInvoker;
import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class PrintAllResourcesTest 

    @Test
    public void name_StateUnderTest_ExpectedBehavior() throws Exception 
        Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();

        dispatcher.getRegistry().addSingletonResource(new MetaService());
        dispatcher.getRegistry().addSingletonResource(new Service());

        MockHttpResponse response = new MockHttpResponse();
        MockHttpRequest request = MockHttpRequest.get("/meta")
                .accept(MediaType.APPLICATION_XML);


        dispatcher.invoke(request, response);

         /*<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
         <resources>
            <resource method="GET">/service/</resource>
            <resource method="POST">/service/</resource>
         </resources>*/
        String result = response.getContentAsString();
    

    @XmlRootElement(name = "resource")
    public static final class JaxRsResource 
        @XmlAttribute String method;
        @XmlValue String uri;

        public JaxRsResource() 

        public JaxRsResource(String method, String uri) 
            this.method = method;
            this.uri = uri;
        

        @Override
        public boolean equals(Object o) 
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            JaxRsResource that = (JaxRsResource) o;

            if (method != null ? !method.equals(that.method) : that.method != null) return false;
            if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false;

            return true;
        

        @Override
        public int hashCode() 
            int result = method != null ? method.hashCode() : 0;
            result = 31 * result + (uri != null ? uri.hashCode() : 0);
            return result;
        
    

    @Path("/service")
    public static final class Service 

        @GET
        @Path("/")
        public String getStuff()
            return "";
        


        @POST
        @Path("/")
        public String postStuff()
            return "";
        
    


    @Path("/meta")
    public static final class MetaService 
        @Context Dispatcher dispatcher;

        @GET
        @Path("/")
        @Wrapped(element = "resources")
        @Formatted
        public Set<JaxRsResource> getAllResources()
            Set<JaxRsResource> resources = new HashSet<JaxRsResource>();

            ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();

            for (Map.Entry<String, List<ResourceInvoker>> entry : registry.getRoot().getBounded().entrySet()) 
                for (ResourceInvoker invoker : entry.getValue()) 
                    ResourceMethod method = (ResourceMethod) invoker;

                    if(method.getMethod().getDeclaringClass() == getClass())
                        continue;
                    

                    for (String verb : method.getHttpMethods()) 
                        String uri = entry.getKey();
                        resources.add(new JaxRsResource(verb, uri));
                    
                
            

            return resources;
        

    

【讨论】:

谢谢...找不到东西...帮了我很多忙。 嗨,很抱歉发布这么旧的答案!我发现您的回答很有帮助,并根据您的代码发布了一个不那么“quick-n-dirty”的方法,并希望与全世界分享:gist.github.com/wonderb0lt/10731371【参考方案2】:

我不得不调整“更干净”的示例,这在开始时非常出色。我正在使用 RestEasy 3.07 并且还希望拥有每个方法的 Path 注释值。我希望这个修改可以对其他人有所帮助。

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.ResourceInvoker;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@Component
@Path("/overview")
public class OverviewResource

private static final class MethodDescription

    private String method;
    private String fullPath;
    private String produces;
    private String consumes;

    public MethodDescription(String method, String fullPath, String produces, String consumes)
    
        super();
        this.method = method;
        this.fullPath = fullPath;
        this.produces = produces;
        this.consumes = consumes;
    



private static final class ResourceDescription

    private String basePath;
    private List<MethodDescription> calls;

    public ResourceDescription(String basePath)
    
        this.basePath = basePath;
        this.calls = Lists.newArrayList();
    

    public void addMethod(String path, ResourceMethodInvoker method)
    
        String produces = mostPreferredOrNull(method.getProduces());
        String consumes = mostPreferredOrNull(method.getConsumes());

        for (String verb : method.getHttpMethods())
        
            calls.add(new MethodDescription(verb, path, produces, consumes));
        
    

    private static String mostPreferredOrNull(MediaType[] mediaTypes)
    
        if (mediaTypes == null || mediaTypes.length < 1)
        
            return null;
        
        else
        
            return mediaTypes[0].toString();
        
    

    public static List<ResourceDescription> fromBoundResourceInvokers(
            Set<Map.Entry<String, List<ResourceInvoker>>> bound)
    
        Map<String, ResourceDescription> descriptions = Maps.newHashMap();

        for (Map.Entry<String, List<ResourceInvoker>> entry : bound)
        
            Method aMethod = ((ResourceMethodInvoker) entry.getValue().get(0)).getMethod();
            String basePath = aMethod.getDeclaringClass().getAnnotation(Path.class).value();

            if (!descriptions.containsKey(basePath))
            
                descriptions.put(basePath, new ResourceDescription(basePath));
            

            for (ResourceInvoker invoker : entry.getValue())
            
                ResourceMethodInvoker method = (ResourceMethodInvoker) invoker;

                String subPath = null;
                for(Annotation annotation : method.getMethodAnnotations())
                
                    if(annotation.annotationType().equals(Path.class))
                    
                        subPath = ((Path) annotation).value();
                        break;
                    
                

                descriptions.get(basePath).addMethod(basePath + subPath, method);
            
        

        return Lists.newLinkedList(descriptions.values());
    


@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List<ResourceDescription> getAvailableEndpoints(@Context Dispatcher dispatcher)

    ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();
    return ResourceDescription.fromBoundResourceInvokers(registry.getBounded().entrySet());


@GET
@Path("/")
@Produces(MediaType.TEXT_html)
public Response getAvailableEndpointsHtml(@Context Dispatcher dispatcher)


    StringBuilder sb = new StringBuilder();
    ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();
    List<ResourceDescription> descriptions = ResourceDescription.fromBoundResourceInvokers(registry.getBounded()
            .entrySet());

    sb.append("<h1>").append("REST interface overview").append("</h1>");

    for (ResourceDescription resource : descriptions)
    
        sb.append("<h2>").append(resource.basePath).append("</h2>");
        sb.append("<ul>");

        for (MethodDescription method : resource.calls)
        
            sb.append("<li> ").append(method.method).append(" ");
            sb.append("<strong>").append(method.fullPath).append("</strong>");

            sb.append("<ul>");

            if (method.consumes != null)
            
                sb.append("<li>").append("Consumes: ").append(method.consumes).append("</li>");
            

            if (method.produces != null)
            
                sb.append("<li>").append("Produces: ").append(method.produces).append("</li>");
            

            sb.append("</ul>");
        

        sb.append("</ul>");
    

    return Response.ok(sb.toString()).build();




(另一方面,也许有一些可用的东西,或者我可以开始工作,为 ServiceStack 做得很好的资源列表和描述建模:http://mono.servicestack.net/Content/Images/MetadataIndex.png)

【讨论】:

【参考方案3】:

如果有人还在寻找 在您的应用上点击“/resteasy/registry”

提供所有已注册端点、关联类/方法等的 XML 输出

仅供参考,resteasy-jaxb-provider 提供此功能

【讨论】:

【参考方案4】:

有一个 RestEasy 插件“stats”,它暴露了.../resteasy/registry

需要在web.xml注册:

<context-param>
    <param-name>resteasy.resources</param-name>
    <param-value>org.jboss.resteasy.plugins.stats.RegistryStatsResource</param-value>
</context-param>

示例响应:

<registry>
    <resource uriTemplate="/resource">
        <delete class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="delete"
                invocations="0"/>
        <head class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="head" invocations="0"/>
    </resource>
    <resource uriTemplate="/locator">
        <locator class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="getLocator"/>
    </resource>
    <resource uriTemplate="/resteasy/registry">
        <get class="org.jboss.resteasy.plugins.stats.RegistryStatsResource" method="get" invocations="2">
            <produces>application/xml</produces>
            <produces>application/json</produces>
        </get>
    </resource>
    <resource uriTemplate="/entry/foo:.*">
        <post class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="post" invocations="0">
            <produces>text/xml</produces>
            <consumes>application/json</consumes>
        </post>
        <put class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="put" invocations="0">
            <produces>text/xml</produces>
            <consumes>application/json</consumes>
        </put>
    </resource>
</registry>

Maven 依赖:

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxb-provider</artifactId>
    <version>3.0.8.Final</version>
</dependency>

参见例如。 EAP docs 和 this EAP 7 Jira

【讨论】:

这里是 RegistryStatsResource javadoc 的更新链接(不是很多):access.redhat.com/webassets/avalon/d/…【参考方案5】:

即使是老帖子,我也在这里给出答案。

这是 JBoss 附带的 RestEasy 的实现。您可以使用它,也可以自己编写。 该实现返回一个具有数组属性的对象,您可以在其中找到每个 RestEasy 资源的 uriTemplate 字符串。

您需要遍历所有条目并获取所需的信息:

RegistryData.entries.get(index).uriTemplate

org.jboss.resteasy.plugins.stats.RegistryStatsResource.get方法的实现:

public RegistryData get() throws JAXBException 
    ResourceMethodRegistry registry = (ResourceMethodRegistry)ResteasyProviderFactory.getContextData(Registry.class);
    RegistryData data = new RegistryData();
    Iterator i$ = registry.getRoot().getBounded().keySet().iterator();

    label85:
    while(i$.hasNext()) 
        String key = (String)i$.next();
        List<ResourceInvoker> invokers = (List)registry.getRoot().getBounded().get(key);
        RegistryEntry entry = new RegistryEntry();
        data.getEntries().add(entry);
        entry.setUriTemplate(key);
        Iterator i$ = invokers.iterator();

        while(true) 
            while(true) 
                if (!i$.hasNext()) 
                    continue label85;
                

                ResourceInvoker invoker = (ResourceInvoker)i$.next();
                if (invoker instanceof ResourceMethod) 
                    ResourceMethod rm = (ResourceMethod)invoker;

                    Object method;
                    for(Iterator i$ = rm.getHttpMethods().iterator(); i$.hasNext(); entry.getMethods().add(method)) 
                        String httpMethod = (String)i$.next();
                        method = null;
                        if (httpMethod.equals("GET")) 
                            method = new GetResourceMethod();
                         else if (httpMethod.equals("PUT")) 
                            method = new PutResourceMethod();
                         else if (httpMethod.equals("DELETE")) 
                            method = new DeleteResourceMethod();
                         else if (httpMethod.equals("POST")) 
                            method = new PostResourceMethod();
                         else if (httpMethod.equals("OPTIONS")) 
                            method = new OptionsResourceMethod();
                         else if (httpMethod.equals("TRACE")) 
                            method = new TraceResourceMethod();
                         else if (httpMethod.equals("HEAD")) 
                            method = new HeadResourceMethod();
                        

                        ((ResourceMethodEntry)method).setClazz(rm.getResourceClass().getName());
                        ((ResourceMethodEntry)method).setMethod(rm.getMethod().getName());
                        AtomicLong stat = (AtomicLong)rm.getStats().get(httpMethod);
                        if (stat != null) 
                            ((ResourceMethodEntry)method).setInvocations(stat.longValue());
                         else 
                            ((ResourceMethodEntry)method).setInvocations(0L);
                        

                        MediaType[] arr$;
                        int len$;
                        int i$;
                        MediaType mediaType;
                        if (rm.getProduces() != null) 
                            arr$ = rm.getProduces();
                            len$ = arr$.length;

                            for(i$ = 0; i$ < len$; ++i$) 
                                mediaType = arr$[i$];
                                ((ResourceMethodEntry)method).getProduces().add(mediaType.toString());
                            
                        

                        if (rm.getConsumes() != null) 
                            arr$ = rm.getConsumes();
                            len$ = arr$.length;

                            for(i$ = 0; i$ < len$; ++i$) 
                                mediaType = arr$[i$];
                                ((ResourceMethodEntry)method).getConsumes().add(mediaType.toString());
                            
                        
                    
                 else 
                    ResourceLocator rl = (ResourceLocator)invoker;
                    SubresourceLocator locator = new SubresourceLocator();
                    locator.setClazz(rl.getMethod().getDeclaringClass().getName());
                    locator.setMethod(rl.getMethod().getName());
                    entry.setLocator(locator);
                
            
        
    

    return data;

另请参阅:WildFly management - list/detect REST endpoints deployed in WildFly

【讨论】:

以上是关于列出 RestEasy 服务的所有公开/可用端点?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的所有 Spring Boot 执行器端点都是公开可用的?

尝试访问公开可用的端点时,部署到 firebase 的 React-app 出现 cors 错误 [重复]

列出所有已部署的 REST 端点(spring-boot、jersey)

如何从私有 AWS ALB 公开 API 端点

RESTEasy 与 Apache Ant

0102-使用 API 网关构建微服务