I have a web service that looks like:
#Path("/ws")
public class Ws {
#GET public Record getOne(#QueryParam("id") Integer id) { return record(id); }
#GET public List<Record> getAll() { return allRecords(); }
}
The idea is that I can either call:
http://ws:8080/ws?id=1 to get a specific record
http://ws:8080/ws to get all available records
However when I use the second URL, the first #GET method is called with a null id.
Is there a way to achieve what I want without using different paths?
I think this can be achieved with Spring using the #RequestMapping(params={"id"}) and #RequestMapping annotations for the first and second methods respectively but I can't use Spring in that project.
Since the path is the same, you cannot map it to a different method. If you change the path using REST style mapping
#Path("/ws")
public class Ws {
#GET #Path("/{id}") public Response getOne(#PathParam("id") Integer id) { return Response.status(200).entity(record(id)).build(); }
#GET public Response getAll() { return Response.status(200).entity(allRecords()).build(); }
then you should use:
http://ws:8080/ws/1 to get a specific record
http://ws:8080/ws to get all available records
Related
Please help me to figure out how Spring parses HTTP GET parameters from the request into the Pageable-suited object without any additional annotations like #RequestBody, #RequestParam, etc.
So, I send a request that looks like this:
GET /questions?page=0&size=2&sort=createdAt,desc.
As an argument of the getQuestions method I get an object consisted of three fields like page, size, sort. But, how this magic actually works?
#RestController
public class QuestionController {
#Autowired
private QuestionRepository questionRepository;
#GetMapping("/questions")
public Page<Question> getQuestions(Pageable pageable) {
return questionRepository.findAll(pageable);
}
#PostMapping("/questions")
public Question createQuestion(#Valid #RequestBody Question question) {
return questionRepository.save(question);
}
// other restful methods
}
I'm creating a Java application using Elastic Search.
Here is the link for my project.
https://github.com/chanakaDe/ensembl-elastic-rest
In this project, I have implemented a rest controller to take data as JSON.
This is the controller class. Now it only has 2 methods. But I need to add some method like this.
#RequestMapping(value = "/find-by/{id}/{param1}/{param2}/{param3}", method = RequestMethod.GET)
public Iterable<Track> findAllWithParams(#PathVariable int id, #PathVariable String param1, #PathVariable String param2, #PathVariable String param3) {
return trackService.someMethodWithParams(id, param1, param2, param3);
}
What I need to do is take some values from user and send them into Elastic server and make a search. I just refered some of these links and got some idea.
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html
TrackService.java and TrackServiceImpl.java are implemented by TrackRepository.java and it's extended by ElasticsearchRepository default class. https://github.com/chanakaDe/ensembl-elastic-rest/blob/master/src/main/java/com/chanaka/book/repository/TrackRepository.java
I need to take values via REST URL and create an object like following and pass that to Elastic Server. How can I implement that with my current project configuration ?
{
"query": {
"constant_score" : {
"filter" : {
"terms" : { "user" : ["kimchy", "elasticsearch"]}
}
}
}
}
This is my TrackService.java interface.
public interface TrackService {
Track save(Track track);
Track findOne(int id);
Iterable<Track> findAll();
}
And also this is my TrackServiceImpl.java class implemented by TrackService.java.
public class TrackServiceImpl implements TrackService {
private TrackRepository trackRepository;
#Autowired
public void setTrackRepository(TrackRepository trackRepository) {this.trackRepository = trackRepository;}
#Override
public Track save(Track track) {
return trackRepository.save(track);
}
#Override
public Track findOne(int id) {
return trackRepository.findOne(id + "");
}
#Override
public Iterable<Track> findAll() {
return trackRepository.findAll();
}
}
Do I need to implement a custom method for that ? Or is there any default methods like findAll() and findOne() ?
Simply pass an object and get the value ?
I think, there's no such existing method and you need to create your own by using QueryBuilders.wrapperQuery(query.toString()) and ElasticsearchTemplate. Just to note, wrapperQuery supports only query not filter. But you can achieve filter context query with constant_score.
Using javaws we can have multiple endpoints leading to the same method. Example:
#Path("/menus")
public class MenuResource {
#Path("/{menuId}/sections")
#Timed #ExceptionMetered
public MenuSectionResource getSections(#InjectParam MenuSectionResource resource) {
return resource;
}
}
#Path("/sections")
public class MenuSectionResource {
#GET
public Section get(#PathParam("menuId") String menuId, #QueryParam("id") String id) {
/// method accessed by GET in /sections or GET in /menus/{menuid}/sections
}
}
I'm trying to use swagger to document both endpoints, but i can only use one
#Api annotation in each class, so i can generate either /sections or /menus/{menuid}/sections. Is it possible to automatically generate both entries in the swagger.json output?
I'm trying to get my head around this RESTful scenario in Jersey: I have two resources, User and Item, and i want to retrieve all the items connected to a certain user and all the users connected to a certain item. In short, there exists a many-to-many relationship between User and Item.
I came across this RESTful design:
All the items connected to a certain user: GET .../users/{user_id}/items
All the users connected to a certain item: GET .../items/{item_id}/users
How can I implement this in Jersey? I found this solution, but it's related to sub-resources nested in the root resource, whereas in my case User and Item are both root resources accessible via their own URIs.
The following should work.
#Path("users")
public class UserResource {
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("{user_id}/items")
public Response getItems(#PathParam("user_id") String userId) {
//get the items and return a response
}
}
#Path("items")
public class ItemResource {
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("{item_id}/users")
public Response getUsers(#PathParam("item_id") String itemId) {
//get the users and return a response
}
}
I decided to implement the solution suggested here, that is to create specific resources that represent the relantionships described above.
The code that models the items-related-to-users relationship is this:
#Path("users/{userId}/items")
public class RelatedItemResource {
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Item> getRelatedItems(#PathParam("userId") String userId) {
// returns list of related items
}
// Other methods
}
The code that models the users-related-to-items relationship is this:
#Path("items/{itemId}/users")
public class RelatedUserResource {
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<User> getRelatedUsers(#PathParam("itemId") String itemId) {
// returns the list of related users
}
// Other methods
}
For all users connected to a certain item, this sounds more like a search criteria to get a list of users. So i would suggest this path : /users?item={itemid}. Given this notation you can really build a flexible search criteria endpoint where every query parameter is optional and is respected if some value is given in it.
I would like to have two GET methods on my Rest resource class.
one would react if query param has value1 and second on value2
Lets say:
#Path("/myApi")
public class MyApiService {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response methodOne(...) {
...
return ...;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response methodTwo(...) {
...
return ...;
}
How to achieve conditional routing for query params
I would like to methodOne() reacts if QueryParam is ?type=one and methodTwo() if QueryParam is ?type=two
Choosing servlet handlers based on QueryParam is not a good aproach, and by default no library gives you oportunity to do so.
The closest that comes to mind is PathParam, that is something like Path("\api\{param1}\{param2}") but it's not what you are looking for.
To achieve want your want just
unregister those methods as servlet handlers (Optional, if you don't need them outside of queryparam selection scope)
define a new one that will choose based on query param
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response methodThree(QueryParam('type') String type) {
return type.equals("type1") ? this.methodOne() : this.methodTwo();
}
You cannot have two methods with identical parameters for the same path.
It's not pretty, but it will work..
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response myMethod(#QueryParam("type") String type){
if(type.equals("one"))
return methodOne();
else
return methodTwo();
}