Spring Rest Controller track entity view count - spring

I need to track entity view count.. for example I have a Product entity and have the following API endpoint:
GET /products/{productID}
In case I want to track a view count of a particular Product should I add additional logic that will increment view count when I call this /products/{productID} endpoint? Or I should introduce a separate endpoint for this purpose?
UPDATED
Maybe I was not clear on my question but my question is about best practice for updating counters with REST API and not about multi-layer architecture. I'm trying to ask the following - should I update the counter by mentioned GET request or I should introduce another API.. let's say POST /products/{productID}/viewings and call it subsequently after the GET in order to update the counter?

Is the view count a property on the Product entity or is it metadata?
If the view count is a property, then consider a separate PUT or PATCH request to update it.
A GET is a safe method and shouldn't update the resource being requested. If a client would prefetch and/or cache the result of that supposedly safe request, you'd have an incorrect view count.
Another question to ask yourself is whether the view count is synonymous with a GET request of that resource. In other words, might your app perform a GET on a resource for a reason other than a user view. If so, that would be another reason to increment the view count in a separate non-safe request.
If the view count is indeed metadata and a GET really does equate to a user view, then I would go ahead and increment the counter on the GET. A separate request has a cost and there are likely other harmless side effects happening (e.g. logging) on the server for each safe request being made.

Ideally it should be subsequent call as GET service is meant for retrieving the value. Since you have almost same logic in POST for updating the count you can make use of same service as given below :
#RequestMapping(value = "/product/{id}", method = { RequestMethod.GET, RequestMethod.POST })
public Product getProduct(#PathVariable String id){
//get and update product
return product;
}

This seems like a good scenario for AOP (Aspect Oriented Programing) as this will allow you to separate this statistic logic from the business logic.
Have a look at Spring doc for more info about AOP and how to achieve that with Spring.
You can then define a pointcut on your controller and have a service for counting (and probably then storing) the data.

Don't put the count in either the controller or in an AOP interceptor; both are terrible solutions for this problem.
You should have a datasource that provides information about a give product (perhaps a database).
You should be using JDBC wrapper to access the database (maybe a DAO written using Hibernate or MyBatis).
You should also have a service that is called by the controller to retrieve a given datasource (as demonstrated in the gene b answer).
Put the access count in either the database code (the DAO) or in the service.
Store the count in the database (perhaps create an AccessedProducts table).

As far as how to get started with a RestController in SpringMVC, the following is a quickstart example (with products/{id} and a JSON/XML req method, JSON is a straight /id and XML is /id.xml).
#RestController
#RequestMapping("products")
public class ProductsController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/json")
public Product getProductInJSON(#PathVariable String id) {
//...Service/DAO fetch based on "id"
Product p = service.getProduct(id);
return p;
}
#RequestMapping(value = "/{id}.xml", method = RequestMethod.GET, produces = "application/xml")
public Product getProductInXML(#PathVariable String id) {
//...Service/DAO fetch based on "id"
Product p = service.getProduct(id);
return p;
}
}
Regarding View Count, yes, I would just add extra logic in the Controller - may be cleaner/simpler than an interceptor.

Related

Spring RequestMapping Controller annotation and create a different absolute path inside the same Controller

From the perpective of Restful Apis, its said its a good choice to design them hierarchical when your database is hierarchical too, above all because the client learns and knows the hierarchical structure of the entities. I mean, for instance, if you have bank clients and accounts, the parent entity would be the clients and the child entities would be the accounts. So :
To get the accounts from the client 1, a right URI could be something like "/clients/1/accounts"
From the perspective of Spring controllers, I should have a ClientController and a AccountController but
The AccountController should process the above request, right?
Could I specify and URI like "accounts/?clientId=1"? Its a bad design?
If I go with the option 1, how to specify this URI in the AccountsController?? If not, should I create another controller just for this and not put this URI in the Account controller?
#RequestMapping("/clients")
public class ClientsController{ }
#RequestMapping("/accounts")
public class AccountsController{
#RequestMapping("/clients/{idClient}/accounts") => **I cannot specify
an absolute path here because
its relative to the RequestMapping annotation in the Controller**
#GetMapping
public #ResponseBody List<Account> getAccounts(){}
}
Thanks!!
There's no hard bound rules it's matter of choice and use cases that decides how we structure our rest uris and using query param or path variables is for constructing meaningful and readable uris.
Suppose if you have.usecase is like to get the list of accounts needs a client ID as mandatory then construct:
GET /clients/{id}/accounts
And put it in clients controller.
But if your usecase is like client id is not mandatory to get list of accounts then construct:
GET /accounts?clientid=1
And put it in accounts controller. Now you can choose to make clientid as required=false request param.
Don't construct deeply nested APIs.. make dumb endpoints eventually you'll end up facing usecases where you'll need to create non nested uris.

Spring calling controller method based on user information

Suppose there are two type of roles in the application -
Admin
Zonal Manager
Admins can get all the office ids while the zonal managers can get only the office assigned under his zone. In the controller I want something like this
#RequestMapping(method = RequestMethod.GET)
Collection<Long> getOfficeIds(){
// returns all office ids in system
}
#RequestMapping(method = RequestMethod.GET, value = "/{zoneId}")
Collection<Long> getOfficeIds(#RequestParam("zoneId") long zoneId){
// returns all office ids in the zone
}
Now I want all my users to make request with the no-arg version only (the first method). The system should get user role before hitting controller and should call appropriate controller method (if admin then call the first method, if zonal manager call the second one with appropriate zone).
The question is , is it possible at all ? If yes then what would be the best way of doing this ? I could try to modify the request in a servlet filter. Is there a way using method argument resolver ?
Per comments, I am posting the answer below.
The best thing to do to achieve your goal is to add a filter which runs before the request is handled by the controller. In this filter, you can apply the appropriate logic to determine the requesting user's role and act accordingly. If you follow the same URL pattern in all of your controllers to handle these different cases, you can simply rewrite the internal URL after determining which case to apply so that it can be handled by the appropriate controller. In this way, you can keep all of your user-role logic in one location and your controller logic can handle their own, separate flows accordingly.
To create such a filter using spring, you may do something like the following:
#Component("accountContextFilter") public class AccountContextFilter extends OncePerRequestFilter {
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException{
//user role and routing logic
}
}
The reference for the logic inside doFilterInternal can be found here: How to use a servlet filter in Java to change an incoming servlet request url?
Simply change the request path accordingly by appending to your route with the role-defined URLs and you're done.

Access data computed in filter for each request inside Spring Controller/Service Class

In our application, all rest apis are of the form:
http://{context}/{product_id}/{rest_url_path}
I have to verify the {product_id} inside a Spring Security Filter/SpringMVC interceptor, by fetching the ProductDetails from DB for the product_id. The ProductDetails will be used inside Spring Controllers/Service classes.
I don't want to fetch the ProductDetails again inside Controllers/Service. So I want to store the ProductDetails object somewhere for that RequestScope.
I have 3 approaches in mind. But each have their pros and cons. Please let me know which one better out of the 3. Also suggest any alternative approach.
Approach-1:
Save the ProductDetails object inside request attribute.
Inside Controller, i can easily get the HttpRequest. Inside Service, I can get HttpRequest by:
#Autowired
HttpServletRequest request;
or
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
if (attribs instanceof ServletRequestAttributes) {
request = ((ServletRequestAttributes) attribs).getRequest();
}
But I don't want to have HTTP request dependency inside Service to make to code more independent from WebLayer logic.
Approach-2:
Use any in memory cache based on product_id to stored ProductDetails
But this I think a over kill only for this use case. Increasing unnecessary dependencies of a cache.
Approach-3:
Store the Object in a ThreadLocal variable to store request scope data.
But not sure if it correct this way.
Let me know an efficient approach to solve this problem
1st and 3rd are suitable for your problem statment but first one is more elegant as data will stored only for current request scope and will get automatically lost when server send response. You can also use threadLocal but you have to be cautious ,if you forget to remove object it will hang around in an environment that uses a thread pool.
The first approach you mentioned is more efficient way to access the same data in both filter and controller even though you have to inject the dependency of HttpservletRequest in SpringController.
If the data is very user specific like other user will not have access to those data in that case you should use ThreadLocal.

ArgumentResolvers within single transaction?

I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.

Storing session data in controller

I'm new to Spring. I'm working on a MVC application that would works as follows:
1) user fills the form with data necessary to create the connection to some service
2) controller gets the data from input, create new object serviceManager and save this object e.g in some HashMap with serviceId
3) next time user wants to use this service, controller using serviceId reads data from HashMap.
So I simply need to store this HashMap throughout the whole session in my controller for future use. What would be the best way to accomplish that? Maybe creating serviceManager object each time and reading data from database is the proper solution? In my controller I'm already using #Autowired fields which perfectly serve the purpose, but they're defined in spring xml and I have to store the data dynamically.
Seems your requirement is kind of same with mine which I should keep the main data in the session and every time get the detail data from client and combine 2 kind of data to retrieve something from database. I just put the main part data in the session and then in the whole session that I can get it. I also try to use #SessionAttribute, but after tried dozens of time, I gave it up, it has a lots of problems. So if you can, I just recomment you to store the data in session, that's the samplest way.
I'm newish to spring myself, but as far as putting this in the session:
#Controller
#SessionAttributes({"myObject"})
public class MyController() {
...
#RequestMapping(value="/foo")
// Corrected as per Costi below
// public String someMethod(#PathVariable MyObject myObject) {
public String someMethod(#ModelAttribute MyObject myObject) {
...
}
}
#SessionAttributes will put a MyObject named myObject into the session, if it's not already there, and the #PathVariable pulls it down so you can use it in the method.
The curlys in session attributes aren't necessary for just one attribute, however, you can specify more than one, comma separated, when you use the array notation (which is to say: the curlys)

Resources