Java - 解析 RESTful 资源 URL 的更好方法
Posted
技术标签:
【中文标题】Java - 解析 RESTful 资源 URL 的更好方法【英文标题】:Java - Better way to parse a RESTful resource URL 【发布时间】:2013-07-24 07:30:19 【问题描述】:我是用 Java 开发 Web 服务的新手(以前我用 php 和 Ruby 做过)。我正在编写一个格式如下的资源:
<URL>/myService/<domain>/<app_name>/<system_name>
如您所见,我有一个三级资源标识符,我正在尝试找出解析它的最佳方法。我添加这个新服务的应用程序没有使用 Jersey 或任何类似的 RESTful 框架。相反,它只是扩展了 HttpServlet。
目前他们正在遵循这样的算法:
致电request.getPathInfo()
将路径信息中的“/”字符替换为“.”字符
使用 String.substring
方法从 pathInfo 字符串中提取此资源的各个信息。
这对我来说似乎不是很优雅,我正在寻找更好的方法。我知道使用 javax.ws.rs 包让这很容易(使用 @Path 和 @PathParam 注释),但使用 Jersey 可能不是一个选项。
仅使用基础 HttpServletRequest 对象和标准 Java 库,有没有比上述方法更好的方法来解析这些信息?
【问题讨论】:
您可以将多个 servlet 映射到不同的 url。可能每个资源一个。 【参考方案1】:我认为首先您需要创建一个框架,用于将 REST 方法和类+方法映射存储在属性文件或内存数据结构中。然后编写一个接受所有 REST 请求的*** servlet。根据从您的上下文开始的 URL,您可以尝试从属性文件/内存数据结构中获取映射,以找出需要调用哪个类及其方法。然后利用反射,您可以调用所需的方法。获取方法响应并将其编组为所需的内容类型格式,然后发送回 servlet 响应输出流。
【讨论】:
【参考方案2】:我和你有同样的问题,因为我没有找到任何合适的库,我决定写URL-RESTify。您可以使用它,或者只是看看编写自己的解决方案,这是一个小项目。
【讨论】:
【参考方案3】:我最近在我的一个应用程序中解决了这个问题。我的网址看起来像这样。
/categories/category/subcategories/subcategory
我的问题是我想将每个 url 模式映射到一个 Java 类,这样我就可以调用正确的类来呈现数据。
我的应用程序使用 Netty,但 URL 解析器不使用任何第三方库。
这允许我做的是解析来自浏览器的 URL,生成具有键值对(在本例中为类别和子类别)的映射,以及为每个实例实例化正确的处理程序独特的 URL 模式。总而言之,只有大约 150 行 Java 代码用于解析、应用程序设置和唯一 URL 模式的定义。
您可以在 GitHub 中查看解析器的代码:https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/util/URLResolver.java
UrlResolver.getValueForUrl 将返回一个 URLData,其中包含您需要的有关您的 URL 的信息: https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/data/URLData.java
设置完成后,我可以将 URL 与 Netty 处理程序相关联:
this.urlResolver.addUrlPattern("/categories", CategoriesHandler.class);
this.urlResolver.addUrlPattern("/categories/category", CategoryHandler.class);
this.urlResolver.addUrlPattern("/categories/category/subcategories", SubCategoriesHandler.class);
this.urlResolver.addUrlPattern("/categories/category/subcategories/subcategory", SubCategoryHandler.class);
在我的处理程序中,我可以简单地获取参数映射:
String category = null;
logger.info("parameterMap: " + getParameterMap());
if (getParameterMap() != null)
category = getParameterMap().get("category");
希望对你有帮助:)
【讨论】:
您的网址已失效 我认为这是 Joachim 代码的新位置:github.com/joachimhs/Conticious/blob/master/Conticious.cms/src/…【参考方案4】:球衣 UriTemplate 怎么样?
import com.sun.jersey.api.uri.UriTemplate;
...
String path = "/foos/foo/bars/bar";
Map<String, String> map = new HashMap<String, String>();
UriTemplate template = new UriTemplate("/foos/foo/bars/bar");
if( template.match(path, map) )
System.out.println("Matched, " + map);
else
System.out.println("Not matched, " + map);
【讨论】:
同匹配我们可以用它来创建url
s 通过替换变量值(比如这些foo
)【参考方案5】:
其他答案中提到的泽西的 UriTemplate 很好,但它是一个大库,它还包含许多其他依赖库。
没有依赖的小解决方案: https://github.com/xitrum-framework/jauter
【讨论】:
【参考方案6】:自己实现(例如检查 main 方法),以防万一您想要自定义实现:
import lombok.AllArgsConstructor;
import lombok.NonNull;
import java.util.*;
public class Template
final List<TemplateElement> templateElements = new ArrayList<>();
public static void main(String[] args)
final Template template = new Template("/hello/who");
final Map<String, String> attributes = template.parse("/hello/world").get();
System.out.println(attributes.get("who")); // world
public Template(@NonNull final String template)
validate(template);
final String[] pathElements = template.split("/");
for (final String element : pathElements)
if (isAttribute(element))
final String elementName = element.substring(1, element.length() - 1); // exclude and
templateElements.add(new TemplateElement(ElementType.ATTRIBUTE, elementName));
else
templateElements.add(new TemplateElement(ElementType.FIXED, element));
public Optional<Map<String, String>> parse(@NonNull final String path)
validate(path);
final String[] pathElements = path.split("/");
if (pathElements.length != templateElements.size()) return Optional.empty();
final Map<String, String> attributes = new HashMap<>();
// ignore the 0th element, it'll always be empty
for (int i = 1; i < templateElements.size(); i++)
final String element = pathElements[i];
final TemplateElement templateElement = templateElements.get(i);
switch (templateElement.type)
case FIXED:
if (!element.equals(templateElement.name)) return Optional.empty();
break;
case ATTRIBUTE:
attributes.put(templateElement.name, element);
break;
return Optional.of(attributes);
private void validate(@NonNull final String path)
if (!path.startsWith("/"))
throw new RuntimeException("A template must start with /"); // a template must start with /
private boolean isAttribute(@NonNull final String str)
return str.startsWith("") && str.endsWith("");
@AllArgsConstructor
class TemplateElement
final ElementType type;
final String name;
enum ElementType
FIXED, ATTRIBUTE
如有错误请指出。谢谢。
【讨论】:
path.split("/") 已经指向问题:路径参数可以使用正则表达式来限制值。如果这样的 RegEx 包含斜线,你就输了。 嗯,有趣的是,在这种情况下,斜杠不会被 url 编码为 %2F 吗(如果它是 url 中某些正则表达式元素的一部分)? 不,RegEx 不是路径本身的一部分。只有路径参数的 VALUES 会被 URL 编码。但是,JAX-RS / WADL 中给出的路径本身并不是一个 URL。以上是关于Java - 解析 RESTful 资源 URL 的更好方法的主要内容,如果未能解决你的问题,请参考以下文章