Restful Path Usage with "/" - java

Is there a difference between #Path having value starting with "/" and without it
I have tested both usage, all work properly.
#Path("message")
public class MessageServices {
#PUT
#Path("sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}
#Path("/message")
public class MessageServices {
#PUT
#Path("/sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}

I've never seen any difference. And the documentation for #Path (http://docs.oracle.com/javaee/7/api/javax/ws/rs/Path.html) says:
Paths are relative. For an annotated class the base URI is the application path, see ApplicationPath. For an annotated method the base URI is the effective URI of the containing class. For the purposes of absolutizing a path against the base URI , a leading '/' in a path is ignored and base URIs are treated as if they ended in '/'.
So there should not be any difference.

According to this tutorial:
A #Path value isn’t required to have leading or trailing slashes (/).
The JAX-RS runtime parses URI path templates the same whether or not
they have leading or trailing spaces.
So it seems like there is no difference.
P.S. I've seen a minor difference once upon a time that was not directly related to JAX-RS runtime itself. One buggy documentation generation framework was generating ugly double-slashed resource links in case of leading slashes in #Path

Related

Can Resteasy look into parameter's type for JAX-RS methods?

We were using Resteasy 3.0.9 for our JAX-RS webservices, and recently switched to 3.0.19, where we started to see a lot of RESTEASY002142: Multiple resource methods match request warnings.
For example, we have methods like:
#Path("/{id}")
public String getSome(UUID id)
#Path("/{id}")
public String getSome(int id)
I'm not sure how it worked in 3.0.9, probably, we just were very lucky as Resteasy seems to select first method from all candidates (and 3.0.19 sorts candidate methods).
One solution is to explicitly specify regex: #Path("/{id : [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}")
But is there a way to somehow tell Resteasy to look into method parameters and construct appropriate regex automatically?
As far as I know, RESTEasy won't take the method parameter type into consideration when matching a request. According the JSR-339 (that RESTEasy implements), this is how the request matching process works:
A request is matched to the corresponding resource method or sub-resource method by comparing the normalized
request URI, the media type of any request entity, and the requested response
entity format to the metadata annotations on the resource classes and their methods. If no matching resource
method or sub-resource method can be found then an appropriate error response is returned. [...]
The JAX-RS implementations must match the requested URI with the #Path annotation values. In the #Path annotation value you can define variables, that are denoted by braces ({ and }).
As part of the request matching, the JAX-RS implementation will replace each URI template variable with the specified regular expression or ([ˆ/]+?) if no regular expression is specified.
To address the situation you mentioned in your question, you should specify a regex to match UUIDs on one resource method:
#Path("{id : [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}")
And you also may consider a regex to match integers on the other resource method:
#Path("{id : \\d+}")

Can I use #GET annotations in two methods in same class?

#GET
#Produces(MediaType.APPLICATION_JSON)
public String getRscSubTypes(){
return AddResourceMysql.getRscSubType();
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getDbTypes() {
return AddResourceMysql.getDbType();
}
This is returning the following exception:
org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during application initialization.
Can you please help me?
How request matching works
Definitely, you can have more than one method annotated with #GET in the same class. However, your current definition is ambiguous.
For more clarification, have a look at the JAX-RS 2.0 specification:
3.7.2 Request Matching
A request is matched to the corresponding resource method or sub-resource method by comparing the normalized request URI, the media type of any request entity, and the requested response entity format to the metadata annotations on the resource classes and their methods. [...]
How to fix it
You need change your method annotations to ensure you have no ambiguity. To do it, you can play with the following annotations:
HTTP method: #GET, #POST, #PUT, #DELETE, #HEAD and #OPTIONS
Request URI: #Path
Media type of any request entity: #Consumes
Requested response entity format: #Produces
To fix it, for example, you can just add a #Path annotation with different values to each method.
If you want to define multiple resource methods, which handle GET requests for the same MIME type, within the same class, you have to specify a different subpath for the methods:
#Path("rcsubtypes")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public String getRscSubTypes()
{
return AddResourceMysql.getRscSubType();
}
#Path("dbtypes")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getDbTypes()
{
return AddResourceMysql.getDbType();
}
The path, specified in the #Path annotation of this method, is a subpath of the path specified in the #Path annotation of the class, which is a subpath of the path you defined for your application.
To explain your behaviour, that always the second method is called, if there is no #Consumes annotation present on the first method: #Consumes defines which media type (set in the Content-Type header of the request) can be accepted by the method. Without a #Consumes annotation all requests are accepted, but i think, if a method specifies an accepted media-type, it will be preferred.
The matching section in the jersey documentation: 3.1. Root Resource Classes

How to design paths for sub-resource within sub-resource in JAX-RS?

I am a complete beginner who is learning to build a RESTful web service. I would like to know how to set the path for sub resource within sub resource in JAX-RS.
I have three resources: profile, message and comment.
I would like my URLs to be as follows.
For profiles
/profiles
For Messages
/profiles/{profileName}/messages
For Comments
/profiles/{profileName}/messages/{messageId}/comments
My resources have paths as follows.
Profile Resource
#Path("/profiles")
public class ProfileResource {
#Path("/{profileName}/messages")
public MessageResource getMessageResource() {
return new MessageResource();
}
}
Message Resource
#Path("/")
public class MessageResource {
#Path("/{messageId}/comments")
public CommentResource getCommentResource() {
return new CommentResource();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Message addMessage(#PathParam("profileName") String profileName, Message message){
return messageService.addMessage(profileName, message);
}
}
Comment Resource
#Path("/")
public class CommentResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Comment postComment(#PathParam("messageId") long messageId, Comment comment) {
return commentService.addComment(messageId, comment);
}
}
But I get the following error,
SEVERE: Servlet [Jersey Web Application] in web application [/messenger] threw
load() exception org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during application
initialization.
[[FATAL] A resource model has ambiguous (sub-)resource method for HTTP method POST
and input mime-types as defined by"#Consumes" and "#Produces" annotations at
Java methods public sokkalingam.restapi.messenger.model.Message
sokkalingam.restapi.messenger.resources.MessageResource.addMessage(java.lang.Strin
g,sokkalingam.restapi.messenger.model.Message) and public
sokkalingam.restapi.messenger.model.Comment
sokkalingam.restapi.messenger.resources.CommentResource.postComment(long,sokkaling
am.restapi.messenger.model.Comment) at matching regular expression /. These two
methods produces and consumes exactly the same mime-types and therefore their
invocation as a resource methods will always fail.;
Questions:
How should I set my paths for my sub resources?
What is a better way to do sub resource within sub resource? Is it
common to do sub-resource within sub-resource?
How should I set my paths for my sub resources?
Get rid of the #Path on the Sub-resource classes. When the class is annotated with path, it is being added as root resource to the Jersey app. So you have a bunch of resources mapped to /, which is giving the error, as there are multiple #POST (with same #Consumes and #Produces) mapped to the same path
With sub-resource classes, you don't need the #Path. It will be ignored, as far as the sub-resource path is concerned.
What is a better way to do sub resource within sub resource? Is it common to do sub-resource within sub-resource?
I don't see any problem with what you are doing.

Spring Restful service URL encoding issue with "."

#RequestMapping(value = "/newLoadtest/{loadtest}", method = RequestMethod.POST)
public #ResponseBody void addNewLoadTest(#PathVariable("loadtest") String loadtest) {
System.out.println(loadtest);
}
This is the code that I have written and if lets say the loadtest string is "test.version" . The period doesn't work for some reason. It prints out "test" only
I think the issue here might be encoding the url but i am not sure how to go about it. Please help
Its spring's smart way to avoid any suffix in URI. You have two ways to disable it.
add ":.+" after your path variable definition.
#RequestMapping(value = "/newLoadtest/{loadtest:.+}", method = RequestMethod.POST)
Another way is to
Use your own "DefaultAnnotationHandlerMapping" and disable "useDefaultSuffixPattern" to false.
Check below quote from Spring reference:
By default Spring MVC automatically performs "." suffix pattern matching so that a controller mapped to /person is also implicitly mapped to /person.. This allows indicating content types via file extensions, e.g. /person.pdf, /person.xml, etc. A common pitfall however is when the last path segment of the mapping is a URI variable, e.g. /person/{id}. While a request for /person/1.json would correctly result in path variable id=1 and extension ".json", when the id naturally contains a dot, e.g. /person/joe#email.com the result does not match expectations. Clearly here ".com" is not a file extension.
if you do not want to use extension content negotiation you can disable it by this line in below code configurer.setUseSuffixPatternMatch(false); or I think the below solution is better to just use registered Suffix, it is up to your needs
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
//configurer.setUseSuffixPatternMatch(false);//this will disable all suffices from negotiation
configurer.setUseRegisteredSuffixPatternMatch(true);
}
}

Jax-rs Regex path

I have this Jax-rs service interface:
#GET
#Path("{id: ^((?!_ah).)*$}")
#Produces(MediaType.TEXT_HTML)
public Response getStuff(#PathParam("id") String id, #Context HttpHeaders headers,
#Context UriInfo uriInfo, #Context SecurityContext securityContext);
The goal of this interface is to catch all character sequence except for:
_ah/foo
_ah/foo/bar
Or anything start starts with _ah
I tried the regex in: http://rubular.com/
And from what it seems it works as expected. However my problem now is that when I access a the supposedly bypassed path I get this:
Could not find resource for relative : /_ah/admin of full path: http://127.0.0.1:8888/_ah/admin
My app runs on GAE, so when running on dev mode, this _ah path is used for management servlets on the SDK runtime.
What am I missing? Isn't it that when the #Path filter does not match it will bypass it? So why do I get Cannot find resource problem?
If I don't put: #Path("{id: ^((?!_ah).)*$}") the servlets under _ah path works fine. Isn't it that the _ah path is already bypassed and should be accessible again?
I ran into this problem while upgrading RestEasy to 3.0.10. I had a #Path("{id}") and #Path("jobs") conflict that came from legacy code. Because of 3rd party apps, I couldn't change my rest endpoints without a lengthy process. Fortunately the regex solution did work for me. Though I can't say why it didn't work for the original poster.
This caused me a few days of headache both figuring out where my problem was and how to solve it. I share this in case you are in a similar situation and changing your rest endpoint isn't a viable option.
Original class:
#Path("device")
public class DeviceApi
{
#PUT
#Path("{id}")
public Device deviceAction(..., Device device) {...}
}
Conflicting regex class:
#Path("device")
public class JobsDeviceApi
{
#PUT
#Path("jobs")
public void moveJobs(..., Jobs jobs)
}
Modified DeviceApi class that works:
#Path("device")
public class DeviceApi
{
#PUT
#Path("{id: ^(jobs)}")
public Device deviceAction(..., Device device) {...}
}
Obviously you can put whatever regex you want in there. I only need it to ignore that single path so I used the KISS principle (Keep it simple stupid).

Categories