Java - 解析 RESTful 资源 URL 的更好方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17840512/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 15:11:28  来源:igfitidea点击:

Java - Better way to parse a RESTful resource URL

javarestservletsrestful-url

提问by dsw88

I'm new to developing web services in Java (previously I've done them in PHP and Ruby). I'm writing a resource that is of the following format:

我是用 Java 开发 Web 服务的新手(以前我用 PHP 和 Ruby 完成过)。我正在编写以下格式的资源:

<URL>/myService/<domain>/<app_name>/<system_name>

As you can see, I've got a three-level resource identifier, and I'm trying to figure out the best way to parse it. The application I'm adding this new service to doesn't make use of Jersey or any RESTful frameworks like that. Instead, it's just extending HttpServlet.

如您所见,我有一个三级资源标识符,我正在尝试找出解析它的最佳方法。我要向其中添加此新服务的应用程序不使用 Jersey 或任何类似的 RESTful 框架。相反,它只是扩展了 HttpServlet。

Currently they're following an algorithm like this:

目前他们正在遵循这样的算法:

  • Call request.getPathInfo()
  • Replace the "/" characters in the path info with "." characters
  • Use String.substringmethods to extract individual pieces of information for this resource from the pathInfo string.
  • 称呼 request.getPathInfo()
  • 用“.”替换路径信息中的“/”字符。人物
  • 使用String.substring方法从 pathInfo 字符串中提取此资源的各个信息。

This doesn't seem very elegant to me, and I'm looking for a better way. I know that using the javax.ws.rs package makes this very easy (using @Path and @PathParam annotations), but using Jersey is probably not an option.

这对我来说似乎不是很优雅,我正在寻找更好的方法。我知道使用 javax.ws.rs 包使这很容易(使用 @Path 和 @PathParam 注释),但使用 Jersey 可能不是一种选择。

Using only the base HttpServletRequest object and standard Java libraries, is there a better way to parse this information than the method described above?

仅使用基本 HttpServletRequest 对象和标准 Java 库,是否有比上述方法更好的解析此信息的方法?

回答by Dongho Yoo

How about jersey UriTemplate?

球衣 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);
}       

回答by Dani Solà

I had the same problem as you and, as I didn't find any suitable library, I decided to write URL-RESTify. You may use it or just take a look to write your own solution, it's a small project.

我和你有同样的问题,因为我没有找到任何合适的库,我决定写URL-RESTify。您可以使用它或只是看看编写自己的解决方案,这是一个小项目。

回答by Joachim H. Skeie

I've recently solved this issue in one of my applications. My URLs look like this.

我最近在我的一个应用程序中解决了这个问题。我的网址看起来像这样。

/categories/{category}/subcategories/{subcategory}

My problem was that I wanted to map each url pattern with a Java class, so that I could call upon the correct class to render the data.

我的问题是我想用 Java 类映射每个 url 模式,以便我可以调用正确的类来呈现数据。

My application uses Netty, but the URL resolver doesn't use any third party libraries.

我的应用程序使用 Netty,但 URL 解析器不使用任何第三方库。

What this allows me to do is to parse the URL that is coming in from the browser, generate a map that has key-value pairs (in this case category, and subcategory), as well as instantiate the correct handler for each unique URL pattern. All in all only about 150 lines of Java code for the parsing, the application setup and the definition of the unique URL patterns.

这允许我做的是解析来自浏览器的 URL,生成具有键值对(在本例中为类别和子类别)的映射,以及为每个唯一的 URL 模式实例化正确的处理程序. 总共只有大约 150 行 Java 代码,用于解析、应用程序设置和唯一 URL 模式的定义。

You can view the code for the resolver in GitHub: https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/util/URLResolver.java

可以在GitHub查看解析器的代码:https: //github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/util/URLResolver.java

UrlResolver.getValueForUrl will return a URLData with the information that you require about your URL: https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/data/URLData.java

UrlResolver.getValueForUrl 将返回一个 URLData,其中包含您需要的 URL 信息:https: //github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/data /URLData.java

Once this is setup, I can associate URLs with Netty Handlers:

设置完成后,我可以将 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);

Inside my Handlers I can simply get the parameter map:

在我的处理程序中,我可以简单地获取参数映射:

String category = null;
logger.info("parameterMap: " + getParameterMap());
if (getParameterMap() != null) {
    category = getParameterMap().get("category");
}

I hope that helps :)

我希望有帮助:)

回答by Ngoc Dao

Jersey's UriTemplate mentioned in other answers is good, but it's a big library and it also includes many other dependency libraries.

其他答案中提到的 Jersey 的 UriTemplate 很好,但它是一个大库,还包括许多其他依赖库。

Tiny solution with no dependency: https://github.com/xitrum-framework/jauter

没有依赖的小解决方案:https: //github.com/xitrum-framework/jauter

回答by Sanjay Verma

Implemented it myself (check the main method for example), just in case if you would want a custom implementation:

自己实现了(例如检查 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
    }
}

Please point out mistakes if any. Thanks.

如有错误请指出。谢谢。

回答by Juned Ahsan

I believe first you need to create a framework for storing the REST method and class+method mappings in a property file or in memory data structue. Then write a top level servlet accepting all of your REST request. Depending on the URL starting from your context, you can try to fetch the mapping from your property file/in memory data structure to find out which class and which of its method need to be called. Then making use of reflection you can call the desired method. Take the method response and marshal it into the desired content-type format and send back to the servlet response output stream.

我相信首先您需要创建一个框架,用于在属性文件或内存数据结构中存储 REST 方法和类+方法映射。然后编写一个接受所有 REST 请求的顶级 servlet。根据从上下文开始的 URL,您可以尝试从属性文件/内存数据结构中获取映射,以找出需要调用哪个类及其方法。然后利用反射你可以调用所需的方法。获取方法响应并将其编组为所需的内容类型格式,然后发送回 servlet 响应输出流。