Spring calling controller method based on user information - spring

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.

Related

How to intercept GET call with Spring Data Rest?

I'm using spring data rest with #RepositoryRestResource where all the verbs are automatically handled for all the entities in the system.
There is no controller necessary for my project.
But I do want to perform certain action before the GET call is made to an entity. What is the best way to do this without writing a custom controller?
There are event handlers I can write in Spring Data Rest like #HandleAfterDelete but there are not handlers for GET.
I'm afraid there is currently no solution which would provide this out of the framework itself. However, there is a pull request which was discussed but not yet implemented as there are still open questions with regard to the universality of findBy* methods.
In case you do not need a that general solution the yet suggested HandlerInterceptor is the way to go…
public class YourInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
// decide on request.getMethod() what to do next
}
}

Why Spring Boot inject same HttpServletResponse object to my Controller method for different request?

I wonder why spring boot inject same response object to my controller method parameter for different request, i use it like follow:
#Controller
#Slf4j
#Profile("default")
#RequestMapping("/test")
public class TestController {
#RequestMapping("/test")
#ResponseBody
public void getDeviceImage(#RequestParam("serialNumber") String serialNumber, HttpServletResponse response) {
return balabala;
}
}
I add a breakpoint before return command, and i find that response object's address is same for different request.
I want to write some thing to response.getOutputStream(), and i found there exists previous buffered data.
HttpServletResponse can be used if you need to add some extra meta information like cookies etc. By default even if you don't specify HttpServletResponse in the arguments, in typical MVC, model is added to the newly constructed response before propagating to the view.
If you just need to return some response back, say a model or entity or a simple JSON, you don't have to manually mess the HttpServletResponse. Unless you want to dig through cookies or headers etc.,. In your code, if you don't need to care about this, you might probably not need it.
As per the API doc for HttpServletResponse:
The servlet container creates an HttpServletResponse object and passes
it as an argument to the servlet's service methods (doGet, doPost,
etc).
What you see is probably the default configurations that Spring sets up.
With #ResponseBody, the return type is directly written back to response.
https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
Finally, i find Response will been reused all the time, and Response's recycle method will been invoked for each request(org.apache.catalina.connector.Response#recycle).
But by default facade&outputStream&writer will not been cleaned, so i make system property "org.apache.catalina.connector.RECYCLE_FACADES" to be "true", after that issue disappears.

Spring Rest Controller track entity view count

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.

how to share the token between classes

I have having a design issue with asp.net web-api and would like to know how to solve this problem
public abstract class BaseApiController<TEntity> : ApiController where TEntity : Entity
{
protected string GetUsername()
{
return Utilities.GetUsername(Request.Headers.Authorization);
}
//some other code
}
public class StakeholderApiController : BaseApiController<Stakeholders>
{
ILogger _logger = new CustomApiLogger("StkhManager", GetUsername())
//some other code
}
now the problem I have is:
I have the BaseApiController which I am using the share certain functionality between all ApiControllers.
And then I have some specified ApiController for certain not shared functionality.
Now while doing logging, I do want to log the logged in user's name, the problem is I have to pass it everytime I create a new instance, is there a way I can make the logged in user's name global for the current api request so that it can be accessed everywhere.
I cannot make it static, otherwise for others request it will give wrong username.
is there a way I can avoid passing it everytime. Given that webapi is stateless, so session cant be used, is there anyother way??
I am using angularjs front end, hence I am NOT using any authorization technique provided by MVC/.net
Note:
I cannot move creation of Logger to base class for certain reasons
This is just one example, I want to use the logged in user's name in many other place. Hence dont want to pass it around.
There is a standard pattern in setting and accessing principal with ASP.NET Web API. From an ApiController such as your BaseApiController, you can just use the User property to retrieve the same. To set it, typically, the HttpRequestContext is obtained from the request object and the principal is set like so.
Request.GetRequestContext().Principal = new ClaimsPrincipal(...);
BTW, you can access the current request object in the Web API pipeline pretty much from anywhere.

Spring WebMVC: interceptor which has access to the method definition and the HttpServletRequest

I'm trying to intercept Spring Controller calls which are annotated, similar to:
#RequestMapping("/my/page")
#AccessRestriction(module = Module.Audit, action = AuditActions.Log)
public ModelAndView myPage() {
// pls type teh codez
}
At this point I want to access both the values of the #AccessRestriction method, the HttpServletRequest object to check if the values match the restrictions and the HttpServletResponse object in order to send a redirect , if applicable. Being able to throw an exception might be suitable as well.
I've looked into Interceptors but they don't offer access to the method, just the handler. What are my options of achieving this?
My suggestion would be to decouple the two concerns, one to check the annotation and throw an exception, another to catch that exception and translate it into a redirect.
The first concern could be done using the Auto-proxy facility, which would apply an AOP-style interceptor to any invocations on your controller objects. They would check for the annotation, validate the invocation, and throw a custom RuntimeException is the conditions are violated.
You could then have a custom HandlerInterceptor which checked for this exception in the afterCompletion method, sending a redirect via the response object if it's present.

Resources