In my Jersey resource i have:
#GET
#Path("/{dataType}/{dataSet}")
public Response search(
#PathParam("dataType") String dataType,
#PathParam("dataSet") String dataSet){
...
}
Instead of strings i want to use my own classes:
#GET
#Path("/{dataType}/{dataSet}")
public Response search(
#PathParam("dataType") DataType dataType,
#PathParam("dataSet") DataSet dataSet){
...
}
However DataSet is dependent on DataType(DataSet uses DataType in it's constructor). Is there a way to do this with Jersey?
You can either use Jersey's built-in transformation using a static fromString() method (see the Jersey documentation), or use a custom provider to handle the path segments. For the latter, you will need a class something like this:
public class MyProvider extends PerRequestTypeInjectableProvider<Context, DataType> {
#Context UriInfo uriInfo;
public Injectable<DataType> getInjectable(ComponentContext componentCtx, Context ctx) {
uri.getPathSegments();
...
}
}
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);
}
}
This code works to access the uriInfo:
#Path("/testing")
public class Testing {
#javax.ws.rs.core.Context UriInfo uriInfo;
#POST
#Path("/test2")
#Produces(MediaType.TEXT_PLAIN)
public Response test2(
#FormParam("sessionId") String sessionId ) {
String currentUserId = Utils.setup(sessionId);
String accessPath = uriInfo.getAbsolutePath().toASCIIString();
System.out.println("The client used this URI to reach this resource method: " + uriInfo.getAbsolutePath().toASCIIString());
// Utils.test3("print this");
return Response.ok("Test 2 ended").build();
}
When I try to access the uriInfo in the called method Utils.test3("print this"); Here:
public class Utils {
#javax.ws.rs.core.Context static UriInfo uriInfo;
public static String setup(String sessionId) {
if (!com.retailapppartners.Utils.validSession(sessionId)) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
String currentUserId = com.retailapppartners.Utils.getUserFromSession(sessionId);
MDC.put("user-id", currentUserId);
return currentUserId;
}
public static void test3(String message) {
System.out.println(message);
String path = uriInfo.getPath();
// System.out.println("The client used this URI: " + uriInfo.getAbsolutePath().toASCIIString());
return;
}
This fails with null pointer exception. I want to see the path uri in the called method to confirm security for all methods in my utils called method. I have searched hi and low for called examples of this. Thanks
Use the #Context annotation to inject an instance of UriInfo into an field variable or method parameter of your resource class
e.g. #1
public String find(#Context UriInfo uri){}
e.g. #2
public class RESTResource{
#Context
private UriInfo uri;
}
Continuing with my comment.. into an answer
Like I said, you can't just decide to inject it anywhere you want. The class being injected into needs to be managed by the JAX-RS runtime, as it's the one that will be doing the injecting. A resource class is managed, a filter provider is managed, that's why you can inject into them. You're utility class is not. And in any case, I don't think it would even work for a [static] "utility" class (even if you were to somehow get it managed) because of the static nature.
Let me just first mention, that UriInfo is specific to each request. static, by nature is "global". You cannot inject it as a static field.
One solution I can see is to make the Utils class (and methods) non-static, and use the underlying injection framework to inject an instance of the Utils class, where ever you need it. This way, if the Utils class is managed, then it should be able to inject the managed UriInfo instance. How this (getting the Utils class managed) will be achieved depends on the implementation you are using, and it's underlying injection framework.
For instance, with Jersey (2), I could do this
public class Utils {
#Context UriInfo uriInfo;
public String test(String s) {
return s + "=" +uriInfo.getAbsolutePath().toString();
}
}
#Path("some")
public class SomeResource {
#Inject
Utils utils;
#GET
public Response getSomething() {
return Response.ok(utils.test("Hello")).build();
}
}
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey.test");
register(new AbstractBinder(){
#Override
protected void configure() {
bind(Utils.class).to(Utils.class);
}
});
}
}
And this works just fine
C:\>curl -v http://localhost:8080/some
Result: Hello=http://localhost:8080/some
Jersey uses HK2 for its injection, so I am able to leverage it to injection of my Utils class.
Now this is probably not the answer you're looking for (as it defeats the purpose of a static utility class), but what you are trying too just can't be done. Either way you go, whether refactoring to pass the UriInfo to your static methods, or the solution above, you will probably have a lot of refactoring to do. I'm surprised you've already created 200 methods using this functionality, before making sure one worked :/
i have a rest controller in a spring boot mvc container
#RestController
public class LoginController {
#RequestMapping("rest/login")
public Response login() {
return Response.GRANTED;
}
public static enum Response {
GRANTED, DENIED;
}
}
I have to use double quotes for checking the return type after request a rest resource. how to avoid the double quotes?
$http.post("rest/login", $scope.data).success(function(data) {
if (data === "\"GRANTED\"") {
alert("GRANTED")
} else if (data === "DENIED") {
alert("DENIED")
};
#RestController
public class LoginController {
#RequestMapping("rest/login")
public String login() {
return Response.GRANTED.name();
}
public static enum Response {
GRANTED, DENIED;
}
}
bring the result I want but I want the type safe return type Response and not String.
Thanks for help.
A #RestController is like a #Controller annotated with #ResponseBody. That is, each handler is implicitly annotated with #ResponseBody. With any reference type other than String (and a few others), the default target content-type is JSON.
The 6 data types in JSON are Object, Array, Number, String, true, false, and null. How would you map an enum constant? The default that Jackson (which backs the default JSON HttpMessageConverter) serializes an enum constant to a JSON String. That's arguably the best matching JSON data type.
You could force it to write the value without quotes by providing your own JsonSerializer
#JsonSerialize(using = ResponseSerializer.class)
public static enum Response {
...
class ResponseSerializer extends JsonSerializer<Response> {
#Override
public void serialize(Response value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeRaw(value.name());
}
}
but I don't recommend it since you wouldn't be producing valid JSON.
You should really consider what others have suggested and use the various HTTP status codes.
Is it possible to have Jersey take a series of HTTP headers and marshall them into a POJO, much as one might do with POST parameters?
If you are using jersey 1.x then you can use #InjectParam,
But I would suggest you to upgrade to 2.x and start using #BeanParam
If you want to get access to a specific #HeaderParam as a String, use the answers provided by #Juned Ahsan or #DJ Spiess. If you want to inject them into a POJO, I would recommend using Jersey's #BeanParam in 2.x.
For example:
#Path("/foo")
public class FooResource {
#GET
#Path("/bar")
public void bar(#BeanParam MyBean myBean) {
// Do something
}
}
public class MyBean {
private String uaCompatible;
public MyBean(#HeaderParam("X-UA-Compatible") String uaCompatible) {
this.uaCompatible = uaCompatible;
}
public String getUacompatible() {
return this.uaCompatible;
}
}
#BeanParam can be replaced with #InjectParam for Jersey 1.x (>=1.4) or #Inject in 1.x earlier than 1.4. javax-#Inject can also be used if you're using a dependency injection framework such as Spring.
Yes, you'd do it like this. (from http://www.mkyong.com/webservices/jax-rs/get-http-header-in-jax-rs/)
#Path("/users")
public class UserService {
#GET
#Path("/get")
public Response addUser(#HeaderParam("user-agent") String userAgent) {
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
}
Yes you can do so using the #HeaderParams
How can I specify a map as one of the parameters of a REST service e.g
#Path("/servicepath")
#Consumes(MediaType.APPLICATION_XML)
public class MyResource {
#POST
public Response getMap(Map<String, List<Object>>) {
//code here
}
}
or
#Path("/servicepath")
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource {
#POST
public Response getMap(Map<String, List<Object>>) {
//code here
}
}
I am using Jersey. Should I implement a MessageBodyReader for that? But implementing a reader for a generic type like Map seems a bad way for me. Maybe I should write a wrapper class on top of the Map object.
What is your ideas? Thanks.
The JAX-RS specification (section 4.2.4) does require that implementors (like jersey) provide a MessageBodyReader implementation for MultivaluedMap<String, String> which is used to consume application/x-www-form-urlencoded mime types. So for example, you can do something like this:
#Path("/servicepath")
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("text/plain")
public String doTheFormThing(MultivaluedMap<String, String> formdata) {
return formdata.toString();
}
Is this not sufficient for what you're trying to do?