I have a requirement to design a RESTful Service using RESTEasy. Clients can call this common service with any number of Query Parameters they would want to. My REST code should be able to read these Query Params in some way. For example if I have a book search service, clients can make the following calls.
http://domain.com/context/rest/books/searchBook?bookName=someBookName
http://domain.com/context/rest/books/searchBook?authorName=someAuthor& pubName=somePublisher
http://domain.com/context/rest/books/searchBook?isbn=213243
http://domain.com/context/rest/books/searchBook?authorName=someAuthor
I have to write a service class like below to handle this.
#Path("/books")
public class BookRestService{
// this is what I currently have, I want to change this method to in-take all the
// dynamic parameters that can come
#GET
#Path("/searchBook")
public Response searchBook(#QueryParam("bookName") String bookName,#QueryParam("isbn") String isbn) {
// fetch all such params
// create a search array and pass to backend
}
#POST
#Path("/addBook")
public Response addBook(......) {
//....
}
}
Sorry for the bad format (I couldn't get how code formatting works in this editor!). As you can see, I need to change the method searchBook() so that it will take any number of query parameters.
I saw a similar post here, but couldn't find the right solution.
How to design a RESTful URL for search with optional parameters?
Could any one throw some light on this please?
The best thing to do in this case would be using a DTO containing all the fields of your search criteria. For example, you mentioned 4 distinct parameters.
Book Name (bookName)
Author Name (authorName)
Publisher Name (pubName)
ISBN (isbn)
Create a DTO containing the fields having the following annotations for every property you want to map the parameters to:
public class CriteriaDTO{
#QueryParam("isbn")
private String isbn;
.
.
Other getter and setters of other properties
}
Here is a method doing that for your reference:
#GET
#Produces("application/json")
#Path("/searchBooks")
public ResultDTO search(#Form CriteriaDTO dto){
}
using following URL will populate the CriteriaDTO's property isbn automatically:
your.server.ip:port/URL/Mapping/searchBooks?isbn=123456789&pubName=testing
A similar question was asked here: How do you map multiple query parameters to the fields of a bean on Jersey GET request?
I went with kensen john's answer (UriInfo) instead. It allowed to just iterate through a set to check which parameters were passed.
Related
I know that generally, we need to do something similar to this for getting a document back from mongodb in spring data:
Define a class and annotate it with #Document:
#Document ("persons")
public class Person
Use MongoTemplete:
mongoOps.findById(p.getId(), Person.class);
The problem is that in runtime I don't know the class type of the document, I just have its string collection name and its string Id. How is it possible to retrieve the document using SpringData? Something like this:
db.myCollectionName.findOne({_id: myId})
The result object type is not a concern, it can be even an object, I just want to map it to a jackson JsonNode.
A possible workaround for this you can use the aggregate function of mongooperation like this
AggregationResults<Object> aggResults = mongoOps.aggregate(newAggregation(match(Criteria.where("_id").is(myId)) ,
myCollectionName, Object.class);
return aggResults.getUniqueMappedResult();
I am using Django Rest Framework for a project and I have a nested serializer like this:
class TopSerializer(serializers.ModelSerializer):
contact = (something goes here)
email = (something goes here)
For POST, PATCH, PUT, DELETE I want to specify these values with a slug. Suppose each class (Contact, Email) has a member called resource_id and that is my slug. For these methods I could use:
class TopSerializer(serializers.ModelSerializer):
contact = serializers.SlugRelatedField(read_only=False, slug_field='resource_id')
email = serializers.SlugRelatedField(read_only=False, slug_field='resource_id')
However, for GET I want to return the embedded objects too, so I could use:
class TopSerializer(serializers.ModelSerializer):
contact = ContactSerializer(read_only=True)
email = EmailSerializers(read_only=True)
So how do I specify in my serializer that contact can be either a slug or a serialized object? Is there a way to do this with just one serializer or must I have two different serializers and use the request.method in the view to select which serializer I use?
Or, should I use something like this:
class TopSerializer(serializers.ModelSerializer):
contact = ContactSerializer(read_only=True)
email = EmailSerializers(read_only=True)
contact_rid = serializers.SlugRelatedField(read_only=False,slug_field=resource_id,queryset=models.Contact.objects.all())
email_rid = serializers.SlugRelatedField(read_only=False,slug_field=resource_id,queryset=models.Email.objects.all())
This way I can use contact_rid and email_rid for POST/PATCH/PUT/DELETE and get contact and email fields back in GET.
Am I on the right track? Other suggestions?
Check out custom fields https://www.django-rest-framework.org/api-guide/fields/#custom-fields
You could define a custom serializer fields that overrides serializers.Field and overrride to_representation to return the fully serialized object and to_internal_value to mimic the behavior of a slugRelatedField.
You are on the right track!
Use one related field for write and another to read the full object is a good approach if you need more details for related objects.
You can also add to the slug field the flag write_only=True if you want the field is used only for write. However, checking this option will not hint selected objects when you are under an update route in Browseable API
Check this anwser
I'm using Objectify and wish to have its Key<> type passed around in my API. I've created an ApiTransformer, but my questions is where to declare it, since the serialized Key<> class is not available, hence I cannot declare its transformer as a class annotation. I tried declaring it in the #Api annotation, but it doesn't work, I still get the error:
There was a problem generating the API metadata for your Cloud Endpoints classes: java.lang.IllegalArgumentException: Parameterized type com.googlecode.objectify.Key<[my package].User> not supported.
The ApiTransformer looks like:
public class KeyTransformer implements Transformer<Key<?>, String> {
public String transformTo(Key<?> in) {
return in.getString();
}
public Key<?> transformFrom(String in) {
return Key.valueOf(in);
}
}
And in my #Api I have:
#Api(name = "users", version = "v1",transformers = {KeyTransformer.class})
Unfortunately you can't. As you said you need to declare it on the Key class, your only chances to make this work are either.
1) Recompile the Key class for objectify with the #transformer annotation.
2) Extend the Key class with your own implementation and define the transformer there.
I don't really like any of those options so the way i usually resolve this is to hide the key object getter (by using #ApiResourceProperty(ignored=AnnotationBoolean.TRUE)) and only expose the id from that key.
That way you get a Endpoints frendly object, the only downside is you'll have to reconstitute the key using Key.create(YourClass.class, longId) manually whenever you need it.
You can add transforms to 3rd party classes by listing the transform in #Api annotation. I'm not dead sure it'll work parameterized class, but I don't see why not.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Api#transformers()
Context
I have the following data structure:
class Birthday(ndb.Model):
day = ndb.IntegerProperty()
month = ndb.IntegerProperty()
year = ndb.IntegerProperty()
class User(ndb.Model):
name = ndb.StringProperty()
birthday = ndb.StructuredProperty(Birthday)
# ... other properties
Problem
When I try to use the populate() method on an instance of User, it gives an error: expecting a Birthday instance instead of a dictionary of params.
If I remove the birthday property, everything works fine: the User instance is populated with the dictionary of params.
Shouldn't the populate() method recognize structured properties and automatically populate them as well?
Any clues?
Thanks
PS: The populate method could also use a forgiving mode on which it ignores unknown properties for which there are references on the params dictionary.
>>Added comments
I'm using a generic REST Handler which is extended for accessing and changing several data types. The extension has to define a method getModel() that returns the model class to access/manipulate. The model class has to implement a few methods, namely create(cls, params).
The POST handler parses params (sent by AngularJS using $resouce -- link below) the following way:
# inside the generic REST Handler
params = json.loads(self.request.body, object_hook=self.datetime_decoder) # parse json params
...
self.getModel().create(params) # invokes the create method of the
The model class implements the create method the following way:
#classmethod
def create(cls, params = None):
obj = cls()
if params:
obj.update(**params)
obj.put()
return True, obj
return False, None
The contents of the JSON dict are:
{"name":"Ana Matos","email":"ana.matos#nvd.com","phone":"+35196983465671","birthday":{"day":1,"month":0,"year":1980},"gender":"FEMALE","groups":["2012/2013"],"serviceProviderId":206133}
JSON contens -- firefox screenshot
AngularJS $resource
Are you reporting a bug or requesting a feature? The populate() method requires its parameter types to match the declared type of the property, which in this case is a Birthday instance.
It would help if you showed the contents of the JSON dict that you are passing to populate() (and exactly how you are passing it).
Possibly the solution is as simple as getting the 'birthday' value from the JSON dict and using it to create a Birthday instance. But I would have to see your code to know for sure.
I want to pass a custom class as parameter to the query method which returns me a collection of entities. I need something like this
[Query]
public IEnumerable<MyEntity> Search(SearchParams params)
{
//do something here
}
public class SearchParams
{
public string FilterParam1 {get; set;}
public string FilterParam2 {get; set;}
public string FilterParam3 {get; set;}
public string FilterParam4 {get; set;}
public string FilterParam5 {get; set;}
...and so on...
}
I tried making SearchParams class available at client side using shared code. But the problem is that no operation(query or invoke) let me create a method where I can pass SearchParams class as it is not a native serializable type.
I have about 15 properties in SearchParams class like this.
I do not want to create a Query operation with 15 parameters.
Please suggest is there's a good workaround for that.
If you are prepared to sacrifice change tracking etc on the returned entities then you could use an 'Invoke' method instead. This will allow you to pass complex types as parameters (as long as they are exposed to the client).
You can expose the type by writing a dummy query method that returns your complex type.
See this other question for more details.
As far as I am aware, the abilitiy to pass complex types as parameters to 'Query' methods is on the roadmap but not currently available.
As Frederick said, one of the main points of RIASvcs is you can send a custom query (LINQ expression) over the wire from the client, to the server, and have it executed there.
It brings all the records to the web server and then filters them.
If I understand you correctly, this isn't true-- bring up Fiddler and see what gets sent back-and-forth, it is indeed filtering on the server and only returning (to the client) what was asked for.
If you read what Martin Fowler says about SOA:
When you're working with a remote interface, such as Remote Facade (388), each call to it is expensive. As a result you need to reduce the number of calls, and that means that you need to transfer more data with each call
So your question has only one answer, yes.
It doesn't make sense to expose complex type like Expression or Func through WCF just because you want to provide a simple syntax in the client.
You have the DataContract, use that to expose a Dto that represent your query.
I think you missed the whole point with the Query method :-)
The Load method which you use to "execute" the query method takes a Query as an argument. Use that one instead of using your own "query object".
For example something like this:
EntityQuery<YourEntity> query = from e in dx.GetYourEntityQuery()
where e.Foo == "something"
select e;
dx.Load<YourEntity>(query);