Java is the best way to parse RESTful resource URLs

I am new to developing web services in Java (I previously did them in PHP and Ruby). I am writing a resource that has the following format:

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

As you can see, I have a three-level resource identifier, and I'm trying to figure out how best to parse it. The application to which I am adding this new service does not use Jersey or any RESTful structures like this. Instead, it simply extends the HttpServlet.

Currently, they perform the following algorithm:

  • Call request.getPathInfo()
  • Replace the characters "/" in the path information with ".". characters
  • Use the String.substring methods to extract individual pieces of information for this resource from the pathInfo string.

This does not seem very elegant to me, and I am looking for the best way. I know that using the javax.ws.rs package makes this very simple (using @Path and @PathParam annotations), but using Jersey is probably not an option.

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

+4
source share
6 answers

What about UriTemplate jerseys?

 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); } 
+9
source

I had the same problem as yours, and since I did not find a suitable library, I decided to write URL-RESTify . You can use it or just look at your own solution, this is a small project.

+2
source

I recently solved this problem 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 to a Java class so that I could call the correct class to render the data.

My application uses Netty, but the URL recognition tool does not use third-party libraries.

What this allows me to do is to analyze the URL that comes from the browser, create a map with key-value pairs (in this case, category and subcategory), and also create an instance of the correct handler for each unique URL pattern. A total of about 150 lines of Java code for parsing, customizing the application, and defining unique URL patterns.

You can view the code for the recognizer on 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 you need for your URL: https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/data/URLData .java

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

  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 just get a parameter map:

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

Hope this helps :)

+2
source

The UriTemplate jersey mentioned in other answers is nice, but it is a large library, as well as many other dependency libraries.

A tiny solution without dependency: https://github.com/xitrum-framework/jauter

+1
source

First I need to create a structure for storing REST methods and class class + method mappings in a property file or in a memory data structure. Then write a top-level servlet that will accept your entire REST request. Depending on the URL starting with your context, you may try to get a mapping from your properties file / in the memory data structure to find out which class and which of its methods need to be called. Then, using reflection, you can call the desired method. Take the method response and move it to the desired content format and send it back to the servlet response output stream.

0
source

I implemented this myself (see, for example, the main method), just in case, if you want a personalized implementation:

 import lombok.AllArgsConstructor; import lombok.NonNull; import java.util.*; public class Template { 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); 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); String[] pathElements = path.split("/"); if (pathElements.length != templateElements.size()) return Optional.empty(); 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 indicate errors, if any. Thank you

0
source

Source: https://habr.com/ru/post/1493257/


All Articles