19

是否可以以简单的方式列出 RestEasy 服务的所有公开/可用端点?

4

5 回答 5

7

有一个 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 文档这个 EAP 7 Jira

于 2016-09-26T10:40:46.387 回答
6

我不得不调整“更清洁”的例子,这是一个很好的开始。我正在使用 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

于 2014-09-04T02:04:02.977 回答
5

编辑:

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


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

这是一个“quick-n-dirty”示例:

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;
        }

    }
}
于 2013-11-05T19:17:37.537 回答
0

即使它是一个旧帖子,我在这里给出我的答案。

这是 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 管理 - 列出/检测 WildFly 中部署的 REST 端点

于 2018-11-13T13:18:53.540 回答
-1

万一有人还在你的应用程序上点击“/resteasy/registry”,它

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

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

于 2014-11-07T12:22:11.923 回答