I'm developing API using Spring Framework and faced a problem that can be solved by simply adding a necessary logic to every place I have it, but I think that there might be an elegant solution to fix it.
I have the following method in my controller:
#GetMapping("/user/{userId}/permissions")
public List<PermissionDto> list(#PathVariable long userId,
#ModelAttribute #Valid PermissionCriteria criteria) {
return permissionService.list(criteria);
}
The thing is that in dto I have a field called userId. It's made not to have a lot of arguments going to the method of the service. But, I want this user id to be set exactly from path since I use the URL that specifies that we are adding permission exactly to specific user resource. It's doable by making addition line that uses setter in the criteria and sets the value of userId. However, now I should never forget to add this line every time I have a case like that. That's why I decided to move it to InitBinder:
#InitBinder(PERMISSIONS_CRITERIA_NAME)
public void permissionsCriteriaInitBinder(WebDataBinder binder) {
PermissionsCriteria criteria = (PermissionsCriteria) binder.getTarget();
Optional.ofNullable(requestHelper.getUserId())
.map(Long::parseLong)
.ifPresent(criteria::setUserId);
}
It works fine. The user ID is set from the path. However, If I specify request parameter and path variable at the same time, even though userId is set from the path in init binder, it's overridden afterwards before it goes to the controller method. So, this one doesn't solve all the issues.
What I want to find, is someplace where the logic can be put to apply to both init binder(I need it for validation) and controller method. Maybe there is a special type of hook or interceptor or at least something to implement to satisfy this conditions?
Related
I am just trying out the first example of GAE Endpoints, I modified the sample API Method to resemble this.
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String[] names) {
My expectation is to receive a array of strings.
Now when I use the Google API Explorer to test this, [https://apis-explorer.appspot.com/apis-explorer/]
it generates API like this
POST https://myprojectid.appspot.com/_ah/api/myApi/v1/sayHi/arg1/arg2/arg3?fields=data
It eventually returns 404 error. Since the endpoint is not recognized.
What am I doing wrong here? In fact explorer shows name as String not String[]. Any help is appreciated!
First things first: does this work when there is a single String parameter? There's some servlet mapping magic that needs to happen to expose endpoints, and if that is not present in the project, things won't work. See this link to make sure your web.xml is as it should be.
Looking at this link, it seems that if your method parameter is a basic type (not a real Java object), and if it is not specifically included in a #Path annotation, there's some uncertainty in what will happen in your Api:
Path parameters are the method parameters included in the path property of the #ApiMethod annotation. If path is unspecified, any parameters not annotated with #Nullable or #DefaultValue will be automatically added to the path (they will be path parameters).
So it seems that by not including "name" in a #Path annotation, the docs don't state what the format of the path will be. The generated descriptor that the Explorer is looking at seems to think the right answer is /names[0]/names[1]/names[2], kind of like C-style varargs. It might be this disconnect that causes your 404 to happen. Can you try by including "name" in a #Path annotation?
Instead of having an array as a parameter of the endpoint method, you should put an object (java bean) which contains an array as a property.
Then you get the object in your method and you just read the property and treat it as an array.
Edit after some more research, following your comment
Indeed when you try to pass an array as a Path parameter it doesn't work. The different elements of your array are added to the URL (as you show in your question) and it generates a 404 Not Found error. The trick is that you should pass this array as a Query parameter and not a Path Parameter. See this doc: https://cloud.google.com/appengine/docs/java/endpoints/parameter-and-return-types#path_parameters
And indeed, if you do something like that it works very well:
#ApiMethod(name = "sayHi",
path = "sayHiWithName")
public MyBean sayHi(#Named("name") String[] names) {
MyBean response = new MyBean();
response.setData("Hi, " + names[0] + names[1]);
return response;
}
Note that the parameter is NOT added to the path (i.e. we don't have a path like sayHiWithName/{name}).
I am implementing EHcache in my project and i have written a method that will remove all entries from the cache. The method works fine, here's the code snippet:
public void removeEntriesFromCache(String cacheName){
CacheManager.getInstance.getCache(cacheName).removeAll();
}
I am eventually going to expose this method as a rest service so that user could call the service along with cache name that needs to be invalidated. However, i would want to be able to do the same using #CacheEvict. I know i can do this:
#CacheEvict(name ="myCache" , allEntries=true)
public void removeEntriesFromCache(){
}
But, this method will remove entries from just one cache , in this case it will remove all cache entries from "myCache". I want to be able to resolve the cache name dynamically at runtime as specified by the user.
Is it possible to achieve something like this:
#CacheEvict(name ="${cacheName}" , allEntries=true)
public void removeEntriesFromCache(String cacheName){
}
OR
#CacheEvict(name ="#cacheName" , allEntries=true)
public void removeEntriesFromCache(String cacheName){
}
Your suggestions would be welcome.
Thanks
#CacheEvict documentation clearly states where you can use SpEl. You can't use SpEl in the name attribute, but you can inject CacheManager to the bean you are annotating now, get the Cache by name and clear it.
cacheManager.getCache(cacheName).clear()
This question already has answers here:
JAX-RS: Multiple paths
(4 answers)
Closed 2 years ago.
Can we have more than one #Path annotation for same REST method i.e. the method executed is the same, but it is executed on accessing more than one URL?
E.g.: I want to run the searchNames() method on both http://a/b/c and http://a/b.
You can't have mutliple #Path annotations on a single method. It causes a "duplicate annotation" syntax error.
However, there's a number of ways you can effectively map two paths to a method.
Regular expressions in #Path annotation
The #Path annotation in JAX-RS accepts parameters, whose values can be restricted using regular expressions.
This annotation:
#Path("a/{parameter: path1|path2}")
would enable the method to be reached by requests for both /a/path1 and /a/path2. If you need to work with subpaths, escape slashes: {a:path1\\/subPath1|path2\\/subPath2}
Serving responses with a redirection status code
Alternatively, you could set up a redirection. Here's a way to do it in Jersey (the reference implementation of JAX-RS), by defining another subresource. This is just an example, if you prefer a different way of handling redirections, feel free to use it.
#Path("basepath")
public class YourBaseResource {
//this gets injected after the class is instantiated by Jersey
#Context
UriInfo uriInfo;
#Path("a/b")
#GET
public Responce method1(){
return Response.ok("blah blah").build();
}
#Path("a/b/c")
#GET
public Response method2(){
UriBuilder addressBuilder = uriInfo.getBaseUriBuilder();
addressBuilder.path("a/b");
return Response.seeOther(addressBuilder.build()).build();
}
}
Using a servlet filter to rewrite URLs
If you're going to need such functionality often, I suggest intercepting the incoming requests using a servlet filter and rewriting the paths on the fly. This should help you keep all redirections in one place. Ideally, you could use a ready library. UrlRewriteFilter can do the trick, as long as you're fine with a BSD license (check out their google code site for details)
Another option is to handle this with a proxy set up in front of your Java app. You can set up an Apache server to offer basic caching and rewrite rules without complicating your Java code.
As explained in Tom's answer, you can not use more than one #Path annotation on a single method, because you will run into error: duplicate annotation at compile time.
I think the simplest way to get around this is to use method overloading:
#Path("{foo}")
public Response rest(#PathParam("foo") final String foo) {
return this.rest(foo, "");
}
#Path("{foo}/{bar}")
public Response rest(#PathParam("foo") final String foo,
#PathParam("bar") final String bar) {
return Response.ok(foo + " " + bar).build();
}
You could also use more different method names if you run into the case where multiple overloaded methods have the signature.
Another solution for your particular example:
http://a/b/c
http://a/b
Let's suppose that:
/a is for the resource class
/b/c and /b are the paths for the methods
because a full path looks like:
<protocol><host><port><app><url-pattern><resource-path><method-path>.
Use optional parameter
#Path("/b{c : (/c)?}")
public Response searchNames(#PathParam("c") String val) {
...
}
The example above works for all examples like:
/b
/b/
/b/c
/b/c/
but when c is provided, the val is /c (it has a / before).
If you want to fix the problem above (to avoid Java parsing), you need something more complex:
#Path("/b{slash : (/)?}{c:((?<=/).*)?}")
which will return only c (not /c) for the 3rd bullet point, but for the 4th bullet point it will return c/ which has to be parsed in Java.
But for your case ("the method executed is the same"), don't worry about parsing because you don't have different actions.
If you are using Spring then try
#RequestMapping(value = {"/def", "/abc"}, method = RequestMethod.POST)
This will work for both /abc and /def.
– sSaroj Nov 17 '17 at 10:13
I have been wrestling with this problem for a while. I would like to use the same Stripes ActionBean for show and update actions. However, I have not been able to figure out how to do this in a clean way that allows reliable binding, validation, and verification of object ownership by the current user.
For example, lets say our action bean takes a postingId. The posting belongs to a user, which is logged in. We might have something like this:
#UrlBinding("/posting/{postingId}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
Now, for the show action, we could define:
private int postingId; // assume the parameter in #UrlBinding above was renamed
private Posting posting;
And now use #After(stages = LifecycleStage.BindingAndValidation) to fetch the Posting. Our #After function can verify that the currently logged in user owns the posting. We must use #After, not #Before, because the postingId won't have been bound to the parameter before hand.
However, for an update function, you want to bind the Posting object to the Posting variable using #Before, not #After, so that the returned form entries get applied on top of the existing Posting object, instead of onto an empty stub.
A custom TypeConverter<T> would work well here, but because the session isn't available from the TypeConverter interface, its difficult to validate ownership of the object during binding.
The only solution I can see is to use two separate action beans, one for show, and one for update. If you do this however, the <stripes:form> tag and its downstream tags won't correctly populate the values of the form, because the beanclass or action tags must map back to the same ActionBean.
As far as I can see, the Stripes model only holds together when manipulating simple (none POJO) parameters. In any other case, you seem to run into a catch-22 of binding your object from your data store and overwriting it with updates sent from the client.
I've got to be missing something. What is the best practice from experienced Stripes users?
In my opinion, authorisation is orthogonal to object hydration. By this, I mean that you should separate the concerns of object hydration (in this case, using a postingId and turning it into a Posting) away from determining whether a user has authorisation to perform operations on that object (like show, update, delete, etc.,).
For object hydration, I use a TypeConverter<T>, and I hydrate the object without regard to the session user. Then inside my ActionBean I have a guard around the setter, thus...
public void setPosting(Posting posting) {
if (accessible(posting)) this.posting = posting;
}
where accessible(posting) looks something like this...
private boolean accessible(Posting posting) {
return authorisationChecker.isAuthorised(whoAmI(), posting);
}
Then your show() event method would look like this...
public Resolution show() {
if (posting == null) return NOT_FOUND;
return new ForwardResolution("/WEB-INF/jsp/posting.jsp");
}
Separately, when I use Stripes I often have multiple events (like "show", or "update") within the same Stripes ActionBean. For me it makes sense to group operations (verbs) around a related noun.
Using clean URLs, your ActionBean annotations would look like this...
#UrlBinding("/posting/{$event}/{posting}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
...where {$event} is the name of your event method (i.e. "show" or "update"). Note that I am using {posting}, and not {postingId}.
For completeness, here is what your update() event method might look like...
public Resolution update() {
if (posting == null) throw new UnauthorisedAccessException();
postingService.saveOrUpdate(posting);
message("posting.save.confirmation");
return new RedirectResolution(PostingsAction.class);
}
I have some actions that requires a specific user permission to be accessed, so I created a method annotation #RequiredPermission and a interceptor to verify if the method that is going to be executed have or not the annotation and if it have verify if the logged user have the permission.
The problem is that I don't know how to get this information from ActionInvocation and neither from ActionContext.
I'm sure that should be one way to do it, cause if not I'd say its probably a not good framework to work with.
Any tip?
The information you need is contained in the ActionProxy, available via ActionInvocation.getProxy().
Once you have the proxy, you have access to the action itself (from the ActionInvocation) and the method name (ActionProxy.getMethod()) as a string.
From then on out it's normal Java reflection.
Method method = action.getClass().getDeclaredMethod(actionmethod);
RequiredPermission permission = method.getAnnotation(RequiredPermission.class);
if (sessionUser.inRoles(permission.getRoles()) {
return invocation.invoke();
}
return Constants.LOGIN_REQUIRED_RESULT;
Or however you want to handle the actual logic.