I have a Jersey based Java servlet:
#Path("foo")
class Foo {
#GET
#Path("bar")
public Response bar(#QueryParam("key") String value) {
// ...
}
}
I can call it in Tomcat just fine as:
http://localhost:8080/container/foo/bar?key=blah
However, in my JerseyTest, using Grizzly, it's not handling the parameters properly. This test case returns a 404 error:
#Test
public void testBar() {
final Response response = target("foo/bar?key=blah").request().get();
}
I suspect the issue is it's looking for a resource named foo/bar?key=blah rather than trying to pass key=blah to the resource at foo/bar. If I pass just "foo/bar" to target(), I get a 500, as the code throws an exception for a null parameter.
I looked through the Jersey Test documentation, and some examples, and I found some cryptic looking stuff that might have been for passing parameters to a GET, but none of it looked like it was assigning values to parameters, so I wasn't positive how I would use it.
How can I pass my value in for that parameter?
JavaDoc to WebTarget.queryParam() should give you an answer to your problem. Basically you need to transform your code to something like:
target("foo/bar").queryParam("key", "blah").request().get()
Related
I am trying to understand how the RESTful Server in Hapi Fhir works and I wanted to implement some #Search methods for Observation resources.
Currently, I have this #Read operation, which successfully works when trying to access the resource (like this: http://localhost:8080/NewFHIRServer/fhir) from the browser:
#Read()
public Observation readObservationById(#IdParam IdDt theId) {
for (Entry<Long, Deque<Observation>> entry : myPatientIdToObservations.entrySet())
{
for (Observation obs : entry.getValue()) {
if (obs.getId().equals(theId)) {
return obs;
}
}
}
throw new ResourceNotFoundException(theId);
}
However, when I try to do something similar for the #Search operation, I am getting errors. I would like to be able to get the response by running the search like this (or similar):
Bundle response = client
.search()
.forResource(Observation.class)
.where(Observation.SUBJECT.hasId("Patient/1"))
.execute();
What parameters do I need to have in my #Read method in order to make this possible? The error I am getting right now is the following:
The FHIR endpoint on this server does not know how to handle GET
operation[Observation] with parameters [[subject]]
and it is obvious why it doesn't work, because my header looks like this:
public Observation searchObservationById(#IdParam IdDt theId)
I have been looking at examples to try to figure this out and I don't quite understand what the syntax in this parameter means:
public List<Patient> getPatient(#RequiredParam(name = Patient.SP_FAMILY) StringParam theFamilyName)...
How would you make the query in order to use this last example?
Thank you
To implement a search method, you need to use #Search instead of #Read on the method. You then use zero-or-more parameters annotated with #OptionalParam or #RequiredParam.
To make your specific example work, you need a search method which implements the _id search parameter, e.g.
#Search
public List<Patient> getPatient(#RequiredParam(name = Patient.SP_RES_ID) StringParam theId) {
}
I have to following endpoint structure in Jersey:
/objects/
/objects/:id
/objects/:id/:field
/objects/:id/:field/:subfield
The IDs I'm using have a very specific format, so I first check if the format is valid before making a request to the database.
Right now I have to put this code in each of the POST, PUT, GET, DELETE functions for each of the functions that has :id as a parameter. So this just means an early return.
if (!isIdValid(id)){
return Response.status(Response.StatusType.BAD_REQUEST)
.entity("The ID you've provided is invalid")
.build();
}
(In reality the error entity is an object containing more information about the error)
And then for each function using the :field or :subfield parameters the code is similar. This checking and error-handling behavior has to be copied every time. And when I start copy-pasting stuff, I start thinking: there should be a better way?
I would like to place the :id checking code at the the /objects/:id level, and then all further nested levels are assumed have a valid ID. The same for the other parameters further nesting down.
I've been looking into using subresource locators, but then you create a function returning a new instance of the subresource. I can't put a conditional return of a Response-object at that level for if the validation fails.
#Path("{id}")
function Class<ObjectFieldResource> getObjectById(#PathParam("id") String id){
return ObjectFieldResource.class;
}
I could start throwing exceptions, but I would rather avoid that, since I don't really consider invalid input to be an exception.
How would such a structure best be implemented? I've looked at bean validation but that doesn't seem to allow me to define validation for my specific format + custom error responses.
Am I missing something in the way subresources should be implemented?
Solution 1
If you can use regexp checks instead of your isIdValid method it's possible to define your resources like this
#POST
#Path("objects/{id:\\d+}")
public Response doSmth(#PathParam("id") String id) {
...
}
In a case of invalid id format caller will have 'Not Found' response status without even reaching your doSmth method.
Obviously, you can use String constants for all equal path values.
final static String ID_RES = "objects/{id:\\d+}";
#POST
#Path(ID_RES)
public Response postSmth(#PathParam("id") String id) {
...
}
...
#GET
#Path(ID_RES)
public Object getSmth(#PathParam("id") String id) {
...
}
The can also read full description of Path#value parameter
Solution 2
Create and register at your REST server javax.ws.rs.container.ContainerRequestFilter implementation with filter method having needed URI checks.
The single filter parameter has ContainerRequestContext type from witch you can call getUriInfo for getting URI and method abortWith(Response response) which can be used for aborting caller request if your resource ids validation was failed.
See Chapter 10. Filters and Interceptors chapter of Jersey Manual.
Say I have a Jersey Resource somewhat similar to this:
#Path("/test")
public class TestResource{
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response test(List<JSONRepresentation> json){
//some logic that gives different responses, all just Strings
}
}
And a RestyGWT Service that consumes it like this:
#Path("api/test")
public interface TestService extends RestService{
#POST
public void test(List<JSONRepresentation> json, MethodCallback<String> callback);
}
The thing is that, when I try to access the Resource using the Service, if the List isn't null or empty I get an Internal Server Error that doesn't have anything to do with the server code because I can't even debug the test method logic.
However, when the List is null or empty, the Resource's logic does what it should and I can debug it without any problems.
The contents of the JSONRepresentation don't really seem to matter for this problem.
I have no idea why this is even happening and I couldn't really find any similar questions around, any help would be really appreciated.
If you want to send Strings back to the client, change your code to:
#Path("/test")
public class TestResource{
#POST
#Produces(MediaType.APPLICATION_JSON)
public String test(List<JSONRepresentation> json){
//some logic that gives different responses, all just Strings
}
}
and change the code of your resource to:
#Path("api/test")
public interface TestService extends RestService{
#POST
public void test(List<JSONRepresentation> json, TextCallback callback);
}
}
hope that helps.
Ok, I somehow figured out why the error happened and how to avoid it.
The thing is, the problem was actually in my json representation, I had defined it with private variables and getters and setters for each one of them (I have some sets and instances of other json representations on it along with other String and primitive variables)
The thing is that, for some reason I'd really want to know more about of, if a variable with a type of another json representation is set as private, this error happens
So, I just set that variable as public and everything worked fine, the odd part is that Collections of another json representation classes work fine even as private variables (primitives, String, and Date work fine like that too).
This question already has answers here:
JAX-RS: Multiple paths
(4 answers)
Closed 2 years ago.
Can we have more than one #Path annotation for same REST method i.e. the method executed is the same, but it is executed on accessing more than one URL?
E.g.: I want to run the searchNames() method on both http://a/b/c and http://a/b.
You can't have mutliple #Path annotations on a single method. It causes a "duplicate annotation" syntax error.
However, there's a number of ways you can effectively map two paths to a method.
Regular expressions in #Path annotation
The #Path annotation in JAX-RS accepts parameters, whose values can be restricted using regular expressions.
This annotation:
#Path("a/{parameter: path1|path2}")
would enable the method to be reached by requests for both /a/path1 and /a/path2. If you need to work with subpaths, escape slashes: {a:path1\\/subPath1|path2\\/subPath2}
Serving responses with a redirection status code
Alternatively, you could set up a redirection. Here's a way to do it in Jersey (the reference implementation of JAX-RS), by defining another subresource. This is just an example, if you prefer a different way of handling redirections, feel free to use it.
#Path("basepath")
public class YourBaseResource {
//this gets injected after the class is instantiated by Jersey
#Context
UriInfo uriInfo;
#Path("a/b")
#GET
public Responce method1(){
return Response.ok("blah blah").build();
}
#Path("a/b/c")
#GET
public Response method2(){
UriBuilder addressBuilder = uriInfo.getBaseUriBuilder();
addressBuilder.path("a/b");
return Response.seeOther(addressBuilder.build()).build();
}
}
Using a servlet filter to rewrite URLs
If you're going to need such functionality often, I suggest intercepting the incoming requests using a servlet filter and rewriting the paths on the fly. This should help you keep all redirections in one place. Ideally, you could use a ready library. UrlRewriteFilter can do the trick, as long as you're fine with a BSD license (check out their google code site for details)
Another option is to handle this with a proxy set up in front of your Java app. You can set up an Apache server to offer basic caching and rewrite rules without complicating your Java code.
As explained in Tom's answer, you can not use more than one #Path annotation on a single method, because you will run into error: duplicate annotation at compile time.
I think the simplest way to get around this is to use method overloading:
#Path("{foo}")
public Response rest(#PathParam("foo") final String foo) {
return this.rest(foo, "");
}
#Path("{foo}/{bar}")
public Response rest(#PathParam("foo") final String foo,
#PathParam("bar") final String bar) {
return Response.ok(foo + " " + bar).build();
}
You could also use more different method names if you run into the case where multiple overloaded methods have the signature.
Another solution for your particular example:
http://a/b/c
http://a/b
Let's suppose that:
/a is for the resource class
/b/c and /b are the paths for the methods
because a full path looks like:
<protocol><host><port><app><url-pattern><resource-path><method-path>.
Use optional parameter
#Path("/b{c : (/c)?}")
public Response searchNames(#PathParam("c") String val) {
...
}
The example above works for all examples like:
/b
/b/
/b/c
/b/c/
but when c is provided, the val is /c (it has a / before).
If you want to fix the problem above (to avoid Java parsing), you need something more complex:
#Path("/b{slash : (/)?}{c:((?<=/).*)?}")
which will return only c (not /c) for the 3rd bullet point, but for the 4th bullet point it will return c/ which has to be parsed in Java.
But for your case ("the method executed is the same"), don't worry about parsing because you don't have different actions.
If you are using Spring then try
#RequestMapping(value = {"/def", "/abc"}, method = RequestMethod.POST)
This will work for both /abc and /def.
– sSaroj Nov 17 '17 at 10:13
My service:
#POST
public String setData(#QueryParam("id") Long is, MyObject payload) {
...
}
or
#POST
public String setData(#PathParam("id") Long is, MyObject payload) {
...
}
My interceptor on the server:
Object read(MessageBodyReaderContext context) throws IOException, WebApplicationException {
Class mypayloadtype = context.getType;
InputStream mypayloadinpustream = context.getInputStream();
Long myidparam = ???????? // how to get the query or path param here?
}
EDIT: To be a bit more concrete:
What I'd like to do is to grab the XML and store it based on the parameters in a separate audit system. Maybe PreProcessInterceptor / PostProcessInterceptor are the better choices?
Any hints or alternative ways to get the param when the xml is still available for preprocessing?
Miguel
I just stumbled over the same problem today. I needed the #PathParams and #QueryParams in the read() method and ended up with something like this:
public class MyInterceptor implements PreProcessInterceptor, MessageBodyReaderInterceptor
{
private static ThreadLocal<UriInfo> uri = new ThreadLocal<UriInfo>();
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
{
uri.set(request.getUri);
...
}
public Object read(MessageBodyReaderContext context)
{
String param = uri.get().getPathParameters().getFirst("myidparam");
...
}
}
Although when thinking about it now - I'm not quite sure, if just using PreProcessInterceptor/PostProcessInterceptor will also do the trick for my (and maybe your) problem. I'll have another look tomorrow.
I am not an expert on the topic but to me it seems as if the MessageBodyReaderContext interface does not really know if it is on the server or the client side, so it cannot expose the request or its parameters / path parts etc.
So as far as I know this is not possible.
If your code knows that it lives on the server side of the rest
communication, maybe you can use a servlet filter to store the request
in a ThreadLocal and then access it from there while the request is
handled, somewhat similar to RequestContextFilter / RequestContextHolder from the spring framework? (Then the request object does not know anything about the annotations of your service, but instead one has to extract the information manually from the request. This means to have the same information in two places, so there has to be a better solution ...)
Edit: after looking at some examples I get the vague feeling that if you want to read the input stream to create an object and add path parameters to it, MessageBodyReaderInterceptor is simply not the way to go. Instead set up a MessageBodyReader which constructs the object from the request body data, and this then will be passed into the public String setData(#PathParam("id") Long is, MyObject payload), assuming that this method is annotated with a #Consumes which matches the #ConsumeMime annotation for the MessageBodyReader. There you might be able in the setData to set the missing id on the object read from the request body. Some examples related to this seem to be here: How to get full REST request body using Jersey? (but for Jersey, not jBoss :-/)
However I am not sure if that works for you, and I also feel I completely overestimated my ability to answer this question appropriately, so I hope someone more knowledgeable comes in with a better solution.