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).
Related
I'm working with Dropwizard, which uses Jersey internally. I have two methods on a controller:
PUT /garbage/[id1,id2,...idN] is intended to take a path parameter that's a list of numeric IDs representing resources to be updated. I'm using a regex-based PathParam here. I've fudged the regex in this example because I don't think it matters, but the point is that a single numeric ID should match the regex.
GET /garbage/[id] fetches data about a single piece of garbage.
Jersey seems to get confused, despite the difference in method. When I query with something like
curl localhost:8080/garbage/1
Jersey gives me a 405 error. If I take the PUT out of the picture (for example, sabotage the path param regex, or remove it entirely), the GET endpoint works fine.
I assume there is some detail in JAX-RS 3.7.2 I'm missing that explains why this should be the case, but I can't figure out what it is.
Here's the code:
#Path("/garbage")
#Produces(MediaType.APPLICATION_JSON)
public class GarbageController {
private static final Logger LOG = LoggerFactory.getLogger(GarbageController.class);
#PUT
#Path("/{params: [\\d,]+}")
#Consumes(MediaType.APPLICATION_JSON)
#Timed
public Response updateGarbage(#PathParam("params") List<PathSegment> params) {
LOG.warn("updateGarbage");
return Response.status(Response.Status.OK).build();
}
#GET
#Path("/{garbageId}")
public Response getGarbageById(#PathParam("garbageId") long garbageId) {
LOG.warn("getGarbage");
return Response.status(Response.Status.OK).build();
}
}
The main purpose of #PathSegment is to handle fragments of the URI which is useful to retrieve Matrix Parameters. For example the method below:
#GET
#Path("/book/{id}")
public String getBook(#PathParam("id") PathSegment id) {...}
Should be able to handle this request:
GET /book;name=EJB 3.0;author=Bill Burke
Because the #PathSegment intercepts the entire URL fragment the GET method seems to be ignored. You can handle the comma-separated IDs on the PUT request with a simple String split:
#PUT
#Path("/{params}")
#Consumes(MediaType.APPLICATION_JSON)
#Timed
public Response updateGarbage(#PathParam("params") String params) {
LOG.warn("updateGarbage ", params.split(","));
return Response.status(Response.Status.OK).build();
}
You can also change the request format to query parameters or implement a Converter/Provider to handle a custom object. All of them should solve the GET not implemented issue.
I believe this is not a case of route priorities between GET and PUT but instead this is related to the #Consumes annotation which cannot be used on a GET request. Either DW is ignoring this endpoint or is converting it into the default POST method, which would explain the 405 response for the GET request.
I figured this out, although I have not traced far enough into Jersey to know why it works. The solution is to rewrite the #GET method to use the same regex syntax as the #PUT. Jersey will handle the type conversion in the method signature, with the note that it will return a 404 if the type conversion fails (ie, GET /garbage/xyz).
#PUT
#Path("/{params: .+}")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateGarbage(#PathParam("params") List<PathSegment> params) {
LOG.warn("updateGarbage");
return Response.status(Response.Status.OK).build();
}
#GET
#Path("/{params: .+}")
public Response getGarbageById(#PathParam("params") long garbageId) {
LOG.warn("getGarbage {}", garbageId);
return Response.status(Response.Status.OK).build();
}
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
I am writing a web service like
#Path("/pathName")
public class LoginServiceComponent {
#GET
#Path("/methodPathName/{param}")
#Produces(MediaType.TEXT_HTML)
public String getVoterByVoterId( #PathParam("param") String param)
{
.................
}
}
Here my url to access web service is http://www.abc.com/pathName/methodPathName/1
Here i have 10 methods.Is there any possibility to remove class level #Path means i have only one web service class in my project.So i dont want to use class level #Param repeatedly.
Thanks in advance...
If you want to avoid the #Path on the class so your URL's don't have the "pathName" in the path, I don't think you can remove the #Path on the class entirely. But I have used the #Path class annotation of #Path("/") and was able to get just URL to be just http://www.abc.com/methodPathName/1 (if that's what you're trying to do).
I have a REST api written with JAX-RS, and I need to add authentication to it. So far all the information I've been able to find about it has suggestions for doing it via spring, which I'm not using. Is there something already existing, or would it be easy to write, something that will let me annotate either a method, or the entire class which would force auth headers to be present?
I'm using tomcat6 and jersey, if that matters.
Something like:
#Path("api")
public class Api {
#GET
#AuthenticationRequired
public Response getInfo(...) {...}
}
I think you want import javax.annotation.Security.RolesAllowed;
The annotation itself looks like this
#Path("/helloworld")
#RolesAllowed({"ADMIN", "ORG1"})
public class helloWorld {
#GET
#Path("sayHello")
#Produces("text/plain")
#RolesAllowed("ADMIN")
public String sayHello() {
return "Hello World!";
}
}
I would manage security at the container level. Here's a good writeup if you happen to be using Apache CXF:
http://cxf.apache.org/docs/secure-jax-rs-services.html
And here's an example for Glassfish:
http://www.butonic.de/2010/06/18/a-simple-jax-rs-security-context-example-in-glassfish/
Here's one more link, which discusses JSR 250 annotations (e.g. #RolesAllowed):
http://www-01.ibm.com/support/knowledgecenter/SSEQTP_8.5.5/com.ibm.websphere.base.doc/ae/twbs_jaxrs_impl_securejaxrs_annotations.html
I've used a regular expression in #Path to achieve overloading and at first I thought it was really neat, but overloading methods is usually not good practice. Does the same apply to RESTful web services? Is there a better way to achieve this using JAX-RS?
So I can now call my getProject REST service by /project/ProjectNumber1000 or /project/12345
#Path("/project")
public class ProjectPropertiesResource
{
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/{name : [a-zA-Z]+}")
public Response getProjectPropertiesByName(#PathParam("name") String name)
{
...
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/{id : \\d+}")
public Response getProjectPropertiesById(#PathParam("id") long id)
{
...
}
}
You can do it, however, only one of the overloads should actually return response body with a 200. The other overloads should return a 303 redirect to the URI that returns the body.
This will ensure that caches only have one copy of the resource and if you do PUT or POST on the main URI you will invalidate the one copy. Otherwise, you can start to get inconsistent results due to different versions existing in the cache.