列出 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 错误 [重复]