Guice provider that can access a JAX-RS POST object - jax-rs

I am attempting to use both Guice with JAX-RS such that I can create a Provider with request scope to instantiate objects. However, in one provider, I wish to be able to access the object that was created from the POST body. For example:
#RequestScoped
class MonkeyProvider implements Provider<Monkey> {
#Inject private Banana banana;
Monkey get() {
// Need access to the Banana that was posted!
}
}
class MyResource {
#Inject private MonkeyProvider monkeyProvider;
#POST
#Path("/test")
void test(Banana banana) {
monkeyProvider.get();
}
}
I know that I can inject UriInfo to get path parameters and query parameters, but is there any way to get the actual body object that was POSTed?

Related

Process custom annotation in implementing class of an Resource interface

I am trying to process a custom annotation on a class that implements an external interface that defines a Resource. The setup is the following:
A Resource interface, I can't modify it:
#Path("/v1")
public interface Resource {
#GET
#Path("/foo")
Response foo();
}
An implementation that I can modify:
public class ResourceImpl implements Resource {
#Override
#CustomAnnotation // has Retention.RUNTIME
public Response foo() {
// foo logic
}
}
I've implemented a filter to try and process the #CustomAnnotation on the overriden foo() method:
#Provider
#ServerInterceptor
#Precedence("SECURITY")
public class CustomAnnotationInterceptor implements ContainerRequestFilter {
#Context
ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
// check if the invoked resource method is annotated with #CustomAnnotation and do logic
}
}
However, when I try to get the matched resource class from the ResourceInfo instance, I get the Resource interface, and when I get the matched method, I get the foo() method from the interface which is lacking the #CustomAnnotation. Is there any way around this?
I'm using RESTEasy as an implementation of JAX-RS.
You could implement an interceptor, where you can get hold of the actual resource (method and class) being called. The interceptor should be bound to your annotation using #InterceptorBinding (see 54.2.4 Binding Interceptors to Components).
// Interceptor
#Interceptor
#CustomAnnotation
#Priority(Interceptor.Priority.APPLICATION)
public class CustomAnnotationInterceptor {
#AroundInvoke
public Object interceptCustomAnnotation(InvocationContext ctx) throws Exception {
CustomAnnotation customAnnotation = null;
// The actual method being called
Method method = ctx.getMethod();
if (method != null) {
customAnnotation = method.getAnnotation(CustomAnnotation.class);
}
// ... do stuff with the annotation
return ctx.proceed();
}
}
To get the instance of the class that implements your interface you could use ctx.getMethod().getDeclaringClass() or ctx.getTarget().getClass().getSuperclass().

c# asp.net core 3 calling different methods from the controller, depending on the request body

I have a controller with the following content (simplified version):
[HttpPost]
public Task<OkResult> Post([FromBody] commonRequest)
{
parser.DoWork(commonRequest);
return Ok();
}
The commonRequest object is populated from the incoming JSON request.
The parser.DoWork method should invoke the creation of a new instance of the class, depending on requestBody.
Here's what it looks like:
public class CommonParser : ICommonParser
{
private readonly ILogger<CommonParser> logger;
private IServiceProvider serviceProvider;
public CommonParser(ILogger<CommonParser> _logger, IServiceProvider _serviceProvider)
{
this.logger = _logger;
this.serviceProvider = _serviceProvider;
}
public void DoWork(CommonRequest commonRequest)
{
ICommonParser parser = (ICommonParser)Activator.CreateInstance(Type.GetType(commonRequest.instance)
, serviceProvider);
parser.DoWork(commonRequest);
}
}
I have three classes whose names are passed through commonRequest.instance. All of these classes implement the ICommonParser interface. Inside these classes, I pass a serviceProvider so that they can get the ILogger inside themselves and use it.
Here is an example constructor of this class:
private readonly ILogger<Parser1> logger;
public Parser1(IServiceProvider serviceProvider)
{
this.logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<Parser1>();
}
As a result, I can send only one message in this way. On the second call, I get a message that serviceProvider.GetRequiredServiceILoggerFactory () has been destroyed.
Please tell me what to do in such cases. I think I'm designing wrong.
From Dependency Injection in ASP.NET Core:
Avoid using the service locator pattern. For example, don't invoke
GetService or GetRequiredService to obtain a service instance when you
can use DI instead.
1) register the logger factory or the logger service, in case of the logger factory
services.AddSingleton<ILoggerFactory, LoggerFactory>();
2) use constructor injection to inject logger factory into the constructor
public Parser1(ILoggerFactory loggerFactory)
{
}
3) you might create a new interface for the parsers (parser1, 2, 3). The parsers implement this interface. Register them as services
public interface IParser
{
void DoWork(CommonRequest commonRequest);
}
services.AddTransient<Parser1>(); // implements IParser
services.AddTransient<Parser2>();
This post gives an answer how to resolve classes implementing the same interface. For getting parser with DI you will actually need IServiceProvider:
_serviceProvider.GetService<Parser1>();

Invoking custom Jackson deserializers inside of other deserializers

I am writing a Spring Boot app (RESTful webservice) that uses Jackson for serialization. I have the following data models that will be sent back and forth between the service and its HTTP clients (hence these will be serialized/deserialized to/from JSON):
public abstract class BaseEntity {
#JsonIgnore
private Long id;
private UUID refId;
// Getters, setters, ctors, etc.
}
public abstract class BaseLookup extends BaseEntity {
private String name;
private String label;
private String description;
// Getters, setters, ctors, etc.
}
public class State extends BaseLookup {
private String abbrev; // "VT", "FL", etc.
// Getters, setters, ctors, etc.
}
public class Contact extends BaseEntity {
private String givenName;
private String surname;
private State state;
// Getters, setters, ctors, etc.
}
public class Account extends BaseEntity {
private Contact contact;
private String code;
// lots of other fields that will be generated server-side
// Getters, setters, ctors, etc.
}
Thus there will be some endpoints for CRUDding Accounts, others for CRUDding Contacts, etc. For instance, the AccountController will expose endpoints for CRUDding Account instances:
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#RequestMapping(method = RequestMethod.POST)
public void createAccount(#RequestBody Account account) {
// Do stuff and persist the account to the DB
}
}
I want to simplify the JSON that HTTP clients must craft in order to create new Account, Contact, etc. instances. At the same time there are fields on those data models that I do not want exposed to the client-side. Things like the BaseEntity#id (which is the PK of the entity in the DB). Or for instance, in the case of State, I just want the client-side to know about (and use) the abbrev field, etc. I don't want them to ever see the other BaseLookup fields or even know about them.
Hence, my end goal is to allow the client to POST the following JSON, and have a custom Jackson deserializer convert that JSON into an Account instance:
{
"contact" : {
"givenName" : "Him",
"surname" : "Himself",
"state" : "NY"
},
"code" : "12345"
}
So you see, like I stated above, this JSON accomplishes several things:
The client-side doesn't provide a BaseEntity#id or BaseEntity#refId when POSTing to create a new instance
For the contact.state field, which is a BaseLookup with several fields (id, refId, name, label, description, abbrev), the user only has to provide the abbrev field, and the deserializer is expected to figure out which State the client is referring to
The Account class actually has many other fields that are inferred/generated server-side; the client doesn't need to know about them in order to create an Account instance
The JSON above is a simplified form of what we would get if we serialized an Account with Jackson's default behavior; this is to make things easier on the client-side and even more secure on the server-side (not exposing PKs, etc.)
The important thing to note here is that the JSON sent to this controller for the contact field is identical to the JSON that will be POSTed to a ContactController for creating new Contact instances.
Here's the problem:
public class AccountDeserializer extends StdDeserializer<Account> {
public AccountDeserializer() {
this(null);
}
public AccountDeserializer(Class<Account> accClazz) {
super(accClazz);
}
#Override
public Account deserialize(JsonParser jsonParser, DeserializationContext dCtx)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.codec.readTree(jsonParser)
Contact contact = ??? // TODO: How to invoke ContactDeserializer here?
String accountCode = node.get("code").asText();
// Generate lots of other Account field values here...
Account account = new Account(contact, accountCode, /* other fields here */);
return account;
}
}
Since I will also have a ContactController (for CRUDding Contact instances irrespective of an associated Account), and because I have similar desires to hide Contact fields from the client-side as well as to simplify the JSON coming into this ContactController#createContact endpoint, I will also need a ContactDeserializer in addition to this AccountDeserializer...
public class ContactDeserializer extends StdDeserializer<Contact> {
// ...etc.
}
This ContactDeserializer will be responsible for converting JSON into Contact instances. But since Account instances also contain Contact instances, and because the "contact JSON" inside the outer "account JSON" will be the same as any JSON that the client sends to any of the "contact endpoints", I'd like to invoke the ContactDeserializer from inside the AccountDeserializer somehow.
That way, when the ContactController receives "contact JSON" to create a new Contact instance, the ContactDeserializer is engaged to get the job done. And, if the AccountController receives "account JSON" to create a new Account instance, then the AccountDeserializer is engaged to get that job done...and it uses the ContactDeserialzer to handle the deserialization of the account JSON's internal contact field as well.
Can this be done?! Can one Jackson deserializer reuse other deserializers inside of it? If so, how? If not, then what's the solution here?!
You can invoke ContactDeserializer by calling the treeToValue method of ObjectCodec. Jackson will automatically pick up the ContactDeserializer for you if you've registered it on your ObjectMapper.
public class AccountDeserializer extends JsonDeserializer<Account> {
#Override
public Account deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
JsonNode node = p.readValueAsTree();
JsonNode contactNode = node.get("contact");
Contact contact = null;
if (contactNode != null) {
contact = p.getCodec().treeToValue(contactNode, Contact.class);
}
return new Account(contact, /* account properties */);
}
}
Edit
If you want to add your deserializers to existing mapper which is created by Spring Boot, you can autowire it in one of your configuration classes and customize as you like.
#Configuration
public class ObjectMapperConfiguration {
#Autowired
public void configureObjectMapper(ObjectMapper mapper) {
SimpleModule module = new SimpleModule()
.addDeserializer(Account.class, new AccountDeserializer())
.addDeserializer(Contact.class, new ContactDeserializer());
mapper.registerModule(module);
}
}

Resteasy and Google Guice: how to use multiple #ApplicationPath and resource with #Injection?

I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out

Spring & Reslet : is it possible to map a URL path component to a method argument?

I'm new to Restlet, but I've followed the tutorial on Restlet's own website and got a basic application up and running. What I'm doing right now is that I'm setting up a basic ServerResource and expose a #Get method.
What I'd like is to be able to invoke /user/{userId} and get the user representation back. Is it possible, somehow, to hand over the mapping of {userId} to Restlet, which in turn would invoke getUser(String userId) in my ServerResource?
Such feature (binding path variables into annotated method parameters) isn't natively supported in the framework. Such mapping in the annotated method signatures is only supported with input representation.
To get the path variables of a request, you can get them from the request object (method getAttribute), as described below:
public class UserServerResource extends ServerResource {
#Get
public User getUser() {
String userId = getAttribute("userId");
User user = (...)
(...)
return user;
}
}
If you want to share this path variable across several methods, you can define it as a instance variable (notice that a new instance of the server resource is created for each request unlike to Spring REST where each controller is a singleton and such variable must be defined in method signatures). We can leverage the method doInit of the server resource, as described below:
public class UserServerResource extends ServerResource {
private String userId;
private User user;
#Override
protected void doInit() throws ResourceException {
super.doInit();
userId = getAttribute("userId");
// for example
user = loadUser(userId);
// throws a status 404 if user can't be found
setExisting(user != null);
}
#Get
public User getUser() {
return user;
}
#Put
public User saveUser(User user) {
saveUser(user);
return user;
}
#Delete
public void deleteUser() {
deleteUser(user);
}
}
If you really want to use a mapping from request elements (like path variables, ...) to method parameters, you should use JAXRS. Restlet provides a support of this specification. Implementing a similar server resource as above but with JAXRS is described below:
#Path("/users/{userId}")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("userId") String userId) {
(...)
}
}
For more details, you can have a look at the corresponding documentation: http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/jaxrs.
Hop it helps,
Thierry