I want to have two resources at URLs: /apps and /apps/runs.
So, I created resources as shown below. I use Spring for object injection. When I use this way, I am getting the 404 error for HTTP get requests on /apps/runs. Am I doing some thing wrong?
Here is my code:
#Scope("prototype")
#Path("/apps")
public class ManufacturersResource {
#GET
#Produces("application/xml")
public List<Applications> getApplications() {
return apps.findAll();
}
}
#Scope("prototype")
#Path("/apps/runs")
public class ManufacturersResource {
#GET
#Produces("application/xml")
public List<ApplicationInstances> getApplicationInstances() {
return appInstances.findAll();
}
}
Jersey won't allow you to have two files share a common prefix, if one is using the prefix as an entire resource url.
So you can move both methods inside the same file, or have /apps be something else like /apps/list
Related
I'm trying to build a simple app that calls an API with quarkus-rest-client.
I have to inject an API Key as a header which is the same for all resources of the API.
So I would like to put the value of this API Key (that depends on the environment dev/qa/prod) in the application.properties file located in src/main/resources.
I tried different ways to achieve this:
Use directly com.acme.Configuration.getKey into #ClientHeaderParam value property
Create a StoresClientHeadersFactory class which implements ClientHeadersFactory interface to inject the configuration
Finally, I found the way described below to make it work.
My question is: Is there a better way to do it?
Here's my code:
StoreService.java which is my client to reach the API
#Path("/stores")
#RegisterRestClient
#ClientHeaderParam(name = "ApiKey", value = "{com.acme.Configuration.getStoresApiKey}")
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores();
}
Configuration.java
#ApplicationScoped
public class Configuration {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
public String getKey() {
return storesApiKey;
}
public static String getStoresApiKey() {
return CDI.current().select(Configuration.class).get().getKey();
}
}
StoresController.java which is the REST controller
#Path("/stores")
public class StoresController {
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores();
}
}
Late to the party, but putting this here for my own reference. There seems to be a difference in using #ClientHeaderParam and #HeaderParam, so I investigated a little further:
According to the Microprofile docs, you can put a compute method for the value in curly braces. This method can extract the property value.
See link for more examples.
EDIT: What I came up with resembles the original, but uses a default method on the interface, so you can at least discard the Configuration class. Also, using the org.eclipse.microprofile.config.Config and ConfigProvider classes to get the config value:
#RegisterRestClient
#ClientHeaderParam(name = "Authorization", value = "{getAuthorizationHeader}")
public interface StoresService {
default String getAuthorizationHeader(){
final Config config = ConfigProvider.getConfig();
return config.getValue("apiKey.stores", String.class);
}
#GET
#Produces("application/json")
Stores getStores();
I will get rid of the Configuration class and use an #HeaderParam to pass your configuration property from your rest endpoint to your rest client. The annotation will then send this property as an HTTP header to the remote service.
Somthing like this should works:
#Path("/stores")
#RegisterRestClient
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores(#HeaderParam("ApiKey") storesApiKey);
}
#Path("/stores")
public class StoresController {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores(storesApiKey);
}
}
Can we have an empty basepath for #Path annotation?
ie. #Path("/")
I want to provide REST api http://servername/abc
#Path("")
public class YourResource {
#Path("/abc")
#GET
public Responce method1(){
return Response.ok("ok").build();
}
}
When I do this, exception is thrown
javax.ws.rs.WebApplicationException
at com.sun.jersey.server.impl.uri.rules.TerminatingRule.accept(TerminatingRule.java:66)
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
UPDATE - I bind in my GuiceServletContextListener like below
bind(YourResource.class).in(Singleton.class);
serve("/abc").with(GuiceContainer.class);
Remove #Path("/") annotation completely from class
and
prefix slash (/) into the method level path annotation, like #Path("/abc")
Please verify whether your URI is mapped to java method as below.
While starting your server, you can see how the URI's mapped into the java methods, something like this in eclipse console...
Mapped "{[/static/transit],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.List<com.xxx.yyy.zzz.domain.Transit> com.xx.yy.zz.controller.abcDataController.getAllTransit()
==================================================================
UPDATE:
According to the java doc below, There are no difference between these 2 examples, both will work.
Either you can use leading slash with both URIs or don't use any leading slash with both URIs, both are same.
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
'/'.
#Path("message")
public class MessageServices {
#PUT
#Path("sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}
OR
#Path("/message")
public class MessageServices {
#PUT
#Path("/sendsms")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms() {
//....
}
}
add a slash to your path ("/"), like this:
#Path("/")
public class YourResource {
...
}
suppose i have some jax-rs resource class:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ResourceA {
#GET
public Something get(#Context UriInfo uriInfo) {
if (...) {
//how to get to ResourceB ?
}
}
}
and i want to conditionally redirect the call to some other jax-rs resource:
public class ResourceB {
#GET
#Path("{identifier}")
public Other get(#PathParam("identifier")String someArg) {
}
}
how do i do this?
note that i dont want this to be visible to the client (so no http redirects) and generally the resource methods i want to redirect to dont share the same signature (they may have path params etc as in the example i gave).
im running jersey 2.6 under apache tomcat (its a spring app, if thats any help)
EDIT - im looking for a jax-rs equivalent of servlet forward. i dont want to do an extra http hop or worry abour instantiating resource classes myself
You can get it using ResourceContext as follows:
#Context
ResourceContext resourceContext;
This will inject the ResourceContext into your Resource. You then get the resource you want using:
ResourceB b = resourceContext.getResource(ResourceB.class);
The Javadoc for ResourceContext is here. You can find a similar question here
I'm not aware of any possibility to do this from a resource method, but if it fits your use case, what you could do is implement your redirect logic in a pre matching request filter, for example like so:
#Provider
#PreMatching
public class RedirectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
String prefix = "/redirect";
String path = uriInfo.getRequestUri().getPath();
if (path.startsWith(prefix)) {
String newPath = path.substring(prefix.length());
URI newRequestURI = uriInfo.getBaseUriBuilder().path(newPath).build();
requestContext.setRequestUri(newRequestURI);
}
}
}
This will redirect every request to /redirect/some/resource to /some/resource (or whatever you pass to requestContext.setRequestUri()) internally, before the resource method has been matched to the request and is executed and without http redirects or an additional internal http request.
With JAX-RS, is it possible to have more than one class assigned to a single path? I'm trying to do something like this:
#Path("/foo")
public class GetHandler {
#GET
public Response handleGet() { ...
}
#Path("/foo")
public class PostHandler {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() { ...
}
This apparently isn't allowed as I get:
com.sun.jersey.api.container.ContainerException: A root resource, class PostHandler, has a non-unique URI template /foo
I can always create one class to handle requests and then delegate to helper classes. I was hoping there was a standard way of doing so.
The JAX-RS spec doesn't forbid such a mapping. For example, Resteasy JAX-RS implementation allows for it. The feature should be jersey specific.
Regarding:
I can always create one class to handle requests and then delegate to helper classes. I was hoping there was a standard way of doing so.
Usually you have the resource classes with the same name as the path:
#Path("/foo")
public class FooResource {
#GET
#Path("/{someFooId}")
public Response handleGet() {
...
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() {
...
}
}
You cannot have multiple resources mapped to the same path. I tried that few days back and landed up at similar error.
I ended up doing subpaths such as /api/contacts for one resource and /api/tags for another.
The only other long way is to create resources in multiple packages and then create different app for each.
I had the similar issue, making the class level #PATH annotation to empty string and moving the resource name to method level #PATH annotation fixed this issue.
#Path("")
public class GetHandler {
#GET
#Path("/foo")
public Response handleGet() {
// impl
}
}
#Path("")
public class PostHandler {
#POST
#Path("/foo")
#Consumes(MediaType.APPLICATION_JSON)
public Response handlePost() {
// impl
}
}
Is it possible to do something like that?
import javax.ws.rs.GET;
import javax.ws.rs.Path;
public class xxx
{
#GET
#Path(value = "path1")
public Response m1()
{
...
}
#GET
#Path(value = "path2")
public Response m1()
{
...
}
}
I'm using RESTEasy btw.
#Path("/{a:path1|path2}")
From resteasy docs:
http://docs.jboss.org/resteasy/docs/1.0.2.GA/userguide/html_single/index.html#_Path_and_regular_expression_mappings
yes you can do that although you will have to rename your methods so that their signature is different.
Update: Check Dieter Cailliau's answer, #Path("/{a:path1|path2}") is probably what you want...
public class BlahResource{
#GET
#Path("path1")
public Response m1(){
return Response.ok("blah").build();
}
#GET
#Path("path2")
public Response m2(){
return this.m1();
}
you can check JSR-311's API and it's reference implementation named "jersey" there:
JSR311 API
Jersey
Some extra details about Path annotation...
As a previous responses state, regular expressions to be used with in the annotated path declaration mapping:
{" variable-name [ ":" regular-expression ] "}
You can declare multiple paths, but there is also a path hierarchy that was not immediately obvious to me whereby the class annotated path prefixes the following method path annotations. One might write the following class for a concise multiple path option which could be useful for resource versioning perhaps.
#Path("/{a:v1|v2}")
#Produces("text/*")
public class BlahResource {
#GET
#Path("/blah")
public Response m1() {
return Response.ok("blah").build();
}
}
Please note the fact that the class "BlahResource" has been declared with the path "/v1" or "/v2" making the resource accessible as:
$ curl localhost:8080/v1/blah
blah
and also
$ curl localhost:8080/v2/blah
blah
You could use sub resources to map two paths to the same resource:
public class MySubResource {
#GET
public Response m1() {
return Response.ok("blah").build();
}
}
#Path("/root")
public class MyRootResource {
#Path("/path1")
public MySubResource path1() {
return new MySubResource();
}
#Path("/path2")
public MySubResource path2() {
return new MySubResource();
}
}