In a RESTEasy application I need to determine at runtime if a certain path can be handled by a specific resource method, based on it's path/query parameters. If it can't be handled I want the request to fallback to other methods. E.g.:
#Path("/")
class MyResource {
#GET
#Path("{path : .*}")
public Response handleRedirects(#PathParam("path") String path) {
if (hasRedirectFor(path)) {
return redirectTo(path);
}
else {
//somehow pretend that this method didn't exist and fallback to the next best match
}
}
#GET
#Path("img/{image}")
public Response handleImage(#PathParam("image") String someParam) {
return createImageResponse(image);
}
#GET
#Path("{template : .*\\.html}")
public Response handleTemplate(#PathParam("template") String template) {
return createTemplateResponse(template);
}
}
Is this somehow possible, without having to use a RequestFilter? (I don't want to use a RequestFilter since then I need to implement URL matching for handleSometimes myself).
EDIT: The comments requested to provide more details, so I changed the example to match closer to my real world situation. I have a typical webserver that handles all sorts of requests, in the above example I reduced that to just images and templates. For legacy reasons there are some incoming links from 3rd parties to URLs that don't (or no longer) exists, but which we don't want to break. We therefore want to serve redirects on those URLs, which is what the handleRedirects function is supposed to do.
Unfortunately there is overlap in the patterns for legacy and supported urls, which prevents me from writing #Path annotations to statically route to the correct method. Moreover, I only know if I can actually generate a redirect from legacy -> new when I examine the path at runtime, if this fails I want to fall back to the other methods. I therefore want to be able to determine in the method itself whether I can handle the request or not, and let RESTEasy fallback to the next matching resource if it can't. Usually this is something were filters would come in handy, but then I loose the ability to automatically extract path parameters or to route to different methods, which I really like to keep.
Related
I have some interceptors that need to check the headers and the authorization in some requests done to my API. For example, some requests should require user authentication (for example, to alter user details from the database) and some don't require authentication (for example, to create a user). Unfortunately, the methods for excluding paths from the interceptors don't depend on the request method.
Right now I have created an Util class that receives an array of strings of paths and methods that should be excluded from the verifications. For example: "POST /api/users" should not be intercepted by my authentication interceptor because it is for creating a user, but "PUT /api/users" should be intercepted because it is for altering an existing user (who should be logged in).
public static Boolean skipVerification(HttpServletRequest request, String... skipRequests) {
for (String string : skipRequests) {
String[] split = string.split(" ");
if(split[0].equals(request.getMethod()) && split[1].equals(request.getRequestURI()))
return true;
}
return false;
}
In the constructor of my interceptor, I add the requests that should be skipped and in the PreHandle method I return true if the request matches any one of them as they didn't need to me intercepted in the first place.
public AuthenticationHeaderInterceptor(String...skipWhen) {
this.skipWhen = skipWhen;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(InterceptorUtils.skipVerification(request, skipWhen))
return true;
///Do Authentication Logic
}
I know that changing the URI for creating and editing users would solve this, but I prefer not to clutter my API with too many URIs and was wondering if there was a cleaner way to solve this.
Looking at the code, I was checking if you would be able to extend InterceptorRegistry, InterceptorRegistration, and MappedInterceptor. You would also have to extend PathMatcher and incorporate code to check methods as well as paths, which PathMatcher isn't meant for. So overall, the way you are doing it currently is probably best. I would also point out the code in Spring's AntPathMatcher has always been very messy and buggy so you probably wouldn't want to take on any task that involved dealing with it.
I'm using Jersey 2.19 to implement a REST API.
I'd like to know how I find out from the Jersey user guide or other specification how I'm supposed to know what the signature of my JAX-RS resource should be.
E.g. for a resource that handles POST requests I've experimented with the following different signatures using examples I've found.
public Response myResource()
public Response myResource(String param)
Both of these are valid in that they compile and run and the method is called under the right conditions.
Can anyone tell me where it is specified what the signatures should be and what the parameters mean? It seems like a straightforward question but I can't find the answer.
As you are saying its a POST request , so it should recieve some data from the Request. So you should expect something in Parameter.
public Response myResource(String param)
But the type of parameter should depend upon actually #Consumes annotation like :-
#Consumes(MediaType.APPLICATION_JSON) : This expects a JSONinput OR
#Consumes(MediaType.APPLICATION_XML) : This expects a XMLinput OR
#Consumes(MediaType.TEXT_PLAIN) : This expects a String plain text input
You annotate your Methods like described in the official documentation.
Also, do not forget to annotate the service-class with #Path
#Path("MyService")
public class MyService
{
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/User")
public List<User> getUser()
{
//Return all users
}
//Inserts new User in JSON Format
#Get
#Path("/User/UserId/{userid}")
public User getUserById(#PathParam("userid") String userid)
{
//Find User with ID in Database and return it
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public User getUserById(User user)
{
//add user to your Database or something
}
}
If you now want to get all users in json format you have to call:
http://ip-address/MyService/user
There is an exact answer to your question, but gird your loins. Because if the Jersey docs are overly vague these are in the extreme opposite direction: written by someone showing off their PhD in abstract algebra it looks to me.
The answer to everything is in the JAX-RS spec, of which Jersey is an implementation. You can download it here as PDF (after you sign away your soul)
The specific answer to how one of those methods is selected instead of the other, is too detailed for me to paste in here, but it's under section "3.7.2 Request Matching"
I won't even try to paste in the mathematical rules used to set up the list of potential methods to match a request, then select from among them. There's no chance of getting them formatted readably in SO.
For your more general questions, the section "3.3 Resource Methods" is much more accessible. Here are a few choice excerpts:
3.3 Resource Methods
...
JAX-RS defines a set of
request method designators for the common HTTP methods: #GET, #POST,
#PUT, #DELETE, #HEAD and #OPTIONS.
...
3.3.1 Visibility: Only public methods may be exposed as resource methods.
...
3.3.2 Parameters: Resource methods MUST have at most one entity parameter ...
3.3.3 Return Type: Resource methods MAY return void, Response, GenericEntity, or another Java type...
etc, etc.
I am designing a REST API that I would like to be localizable in the future.
Therefore I am defining the URLs to be of the form
/en/<resource_url>
With the intention of being able to support
/fr/<resource_url>
In the future if need be.
However I only want to define each resource url service once. Therefore I figure I need to get the URL parsed and rewritten without the language piece of the URL before it is matched to services. Finally that language should be made available to the services somehow for them to localize if necessary.
How can I achieve this?
I am using Jersey 1.17 inside Jetty container embedded in a larger server process.
You can make the /en/ or the /fr/ part a variable. Then set your locale to the value of the variable. Here's an example:
#Path("/{locale}/username")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("locale") String locale) {
...
}
}
But that may not be the best way to go about it. After answering, I found this other SO question that is a better way to solve the problem: Getting the client locale in a jersey request With this way, you don't need to add this to the URL. Just make the client set a header.
Best practice for REST resource versioning is putting version information into Accept/Content-Type headers of HTTP request leaving URI intact.
Here is the sample request/response to REST API for retrieving system information:
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v1+json
<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v1+json
{
“session-count”: 19
}
Pay attention that version is specified in MIME type.
Here is another request/response for version 2:
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v2+json
<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v2+json
{
“uptime”: 234564300,
“session-count”: 19
}
See http://barelyenough.org/blog/tag/rest-versioning/ for more explanation and examples.
Is it possible to implement this approach easily in Java-targeted JAX-RS based implementations, such as Jersey or Apache CXF?
The goal is to have several #Resource classes with the same #Path value, but serving the request based on actual version specified in MIME type?
I've looked into JAX-RS in general and Jersey in particlaur and found no support for that. Jersey doesn't give a chance to register two resources with the same path. Replacement for WebApplicationImpl class needs to implemented to support that.
Can you suggest something?
NOTE: It is required for multiple versions of the same resource needs to be available simultaneously. New versions may introduce incompatibale changes.
JAX-RS dispatches to methods annotated with #Produces via the Accept header. So, if you want JAX-RS to do your dispatching, you'll need to leverage this mechanism. Without any extra work, you would have to create a method (and Provider) for every media type you wish to support.
There's nothing stopping you from having several methods based on media type that all call a common method to do that work, but you'd have to update that and add code every time you added a new media type.
One idea is to add a filter that "normalizes" your Accept header specifically for dispatch. That is, perhaps, taking your:
Accept: application/vnd.COMPANY.systeminfo-v1+json
And converting that to, simply:
Accept: application/vnd.COMPANY.systeminfo+json
At the same time, you extract the version information for later use (perhaps in the request, or some other ad hoc mechanism).
Then, JAX-RS will dispatch to the single method that handles "application/vnd.COMPANY.systeminfo+json".
THAT method then takes the "out of band" versioning information to handle details in processing (such as selecting the proper class to load via OSGi).
Next, you then create a Provider with an appropriate MessageBodyWriter. The provider will be selected by JAX-RS for the application/vnd.COMPANY.systeminfo+json media type. It will be up to your MBW to figure out the actual media type (based again on that version information) and to create the proper output format (again, perhaps dispatching to the correct OSGi loaded class).
I don't know if an MBW can overwrite the Content-Type header or not. If not, then you can delegate the earlier filter to rewrite that part for you on the way out.
It's a little convoluted, but if you want to leverage JAX-RS dispatch, and not create methods for every version of your media type, then this is a possible path to do that.
Edit in response to comment:
Yea, essentially, you want JAX-RS to dispatch to the proper class based on both Path and Accept type. It is unlikely that JAX-RS will do this out of the box, as it's a bit of an edge case. I have not looked at any of the JAX-RS implementations, but you may be able to do what you want by tweaking one of the at the infrastructure level.
Possibly another less invasive option is to use an age old trick from the Apache world, and simply create a filter that rewrites your path based on the Accept header.
So, when the system gets:
GET /resource
Accept: application/vnd.COMPANY.systeminfo-v1+json
You rewrite it to:
GET /resource-v1
Accept: application/vnd.COMPANY.systeminfo-v1+json
Then, in your JAX-RS class:
#Path("resource-v1")
#Produces("application/vnd.COMPANY.systeminfo-v1+json")
public class ResourceV1 {
...
}
So, your clients get the correct view, but your classes get dispatched properly by JAX-RS. The only other issue is that your classes, if they look, will see the modified Path, not the original path (but your filter can stuff that in the request as a reference if you like).
It's not ideal, but it's (mostly) free.
This is an existing filter that might do what you want to do, if not it perhaps can act as an inspiration for you to do it yourself.
With current version of Jersey, I would suggest an implementation with two different API methods and two different return values that are automatically serialised to the applicable MIME type. Once the requests to the different versions of the API are received, common code can be used underneath.
Example:
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public VersionOneDTO get(#PathParam("id") final String id) {
return new VersionOneDTO( ... );
}
#GET
#Path("/{id}")
#Produces("application/vnd.COMPANY.systeminfo-v2+json;qs=0.9")
public VersionTwoDTO get_v2(#PathParam("id") final String id) {
return new VersionTwoDTO( ... );
}
If method get(...) and get_v2(...) use common logic, I would suggest to put that in a common private method if it's API related (such as session or JWT handling) or else in a common public method of a Service Layer that you access via inheritance or Dependency Injection. By having two different methods with different return types, you ensure that the structure returned is of correct type for the different versions of the API.
Note that some old client may not specify Accept header at all. That means implicitly that they would accept any content type, thus any version of your API. In practice, this is most often not the truth. For this reason you should specify a weight to newer versions of the API using the qs extension of the MIME type as shown in the #Produces annotation in the example above.
If you are testing with restAssured it would look something like this:
import static com.jayway.restassured.RestAssured.get;
import static com.jayway.restassured.RestAssured.given;
#Test
public void testGetEntityV1() {
given()
.header("Accept", MediaType.APPLICATION_JSON)
.when()
.get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 1 was called
;
}
#Test
public void testGetEntityV1OldClientNoAcceptHeader() {
get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 1 was called
;
}
#Test
public void testGetEntityV2() {
given()
.header("Accept", "application/vnd.COMPANY.systeminfo-v2+json")
.when()
.get("/basepath/1")
.then()
.assertThat()
... // Some check that Version 2 was called
;
}
One possible solution is to use one #Path with
Content-Type:
application/vnd.COMPANY.systeminfo-{version}+json
Then, inside the method of the given #Path you can call the version of the WebService
If you're using CXF, you could use the technique specified here to build a new serialization provider (building off the existing infrastructure) which produces the data in the specific format desired. Declare a couple of those, one for each specific format that you want, and use the #Produces annotation to let the machinery handle the rest of the negotiation for you, though it might also be an idea to support the standard JSON content type too so that normal clients can handle it without needing to grok your specialness. The only real question then becomes what is the best way to do the serialization; I presume you can figure that out for yourself…
[EDIT]: Further digging in the CXF documentation leads to the revelation that both the #Consumes and #Produces annotations are considered to be axes for doing selection. If you want to have two methods that handle the production of the response for different media types, you most certainly can. (You'll have to add the serialization and/or deserialization providers if you're using custom types, but you can do the delegation of the majority of the work to the standard providers.) I'd still like to caution that you should still ensure that the resource indicated by the path should be the same in both cases; to do otherwise is not RESTful.
You should be able to use different classes with the same path provided they consume/produce different media types. So this should work with any jax-rs provider:
#Path("/api/system-info")
#Consumes("application/vnd.COMPANY.systeminfo-v1+json")
#Produces("application/vnd.COMPANY.systeminfo-v1+json")
public class SystemInfoResourceV1 {
}
and
#Path("/api/system-info")
#Consumes("application/vnd.COMPANY.systeminfo-v2+json")
#Produces("application/vnd.COMPANY.systeminfo-v2+json")
public class SystemInfoResourceV2 {
}
I always wondered why there exists no removeParameters() method in Servlet API.
What could be the motive behind this design?
Here is a scenario: I am posed with a challenge in a proprietary MVC framework that I am compelled to use. This framework uses a Controller Servlet that hosts an algorithm in it's post method:
doPost() {
//create instance of action - just like struts action
action.init
action.preexecution
if(redirection state is not set)
action.process
action.postprocess
action.finish
}
The only way I can skip process of any particular action would be by setting a redirection url. The Controller Servlet is FINAL. Now, when I do a requestdispatcher.forward from say the preexecution method of an action, the controller will go ahead and execute the rest of the methods and not skip the rest. I cannot change this behavior, neither can I set the redirect, coz I need to do a forward. It works fine as long as I am not forwarding request to the same action. When a request is forwarded to the same action, the http parameters are all the same. This would take it into a never ending loop. Hence, I am compelled to add extra parameters indicating that it is a repeat request and should be treated differently.
Not sure if my problem made sense, but thought this is a good forum to post the same.
Umm... because it would serve no purpose? Request parameters are sent by the client to the server. The server is free to ignore them, but what practical effect would you expect such a removeParameter() method to have?
Edit: Request parameters are meant for the communication between server and client. For server-internal communication, you can use request attributes, which can be set and removed.
EDIT: McDowell reminded me of HttpServletRequestWrapper, so I'm changing the below to make it a little less work... Thanks McD!
You can decorate the request to "hide" parameters you don't want and/or add extra parameters.
Something like (off the top of me head -- no compiling so the API might be a tweak off...)
public class MyParameterHider extends HttpServletRequestWrapper {
public MyParameterHider(HttpServletRequest request) {
super(request);
}
public String getParameter(String name) {
if ("parameterToHide".equals(name))
return null;
return realRequest.getParameter(name);
}
// similar for getParameterNames and getParameterMap - don't include the hidden parm
// all other methods are strictly pass-through and are automatically
// handled by HttpServletRequestWrapper
}
In your forward, just wrap the request in a ParameterHider when calling doFilter:
dispatcher.forward(new MyParameterHider(request), response);
Patterns FTW!
Hope this helps!