I need to implement a controller that has a command object that is backing a filtering form for a search across multiple entries.
The problem is that the i was asked to do that without using POST request, instead using GET request only, and there before loosing the functionality of the default data binding that springs makes happily for us.
So i tried to implement a method, inside my controller, that looks like this:
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (isSearchRequest(request)) {
MyCommandObject myCommandObject = (MyCommandObject) getCommand(request);
System.out.println(managePositionsForm);
}
return super.handleRequestInternal(request, response);
}
But the getCommand returns me a brand new CommandObject with no values, despite that the values are present in the request object (i could retrieve then using the getParameter method of HttpServletRequest). But there isn't any binding.
So the question :
1) Is there any way to archive this?
2) Also is very important, that all the values in the form, are lost and, eventually (if this problem is solved) i will need to "persist" the filters for the users in order to avoid re entering after the first search.
Auto Response : setSessionForm(true); looks like can do the work! (According to javadoc)
Thanks to all!
Greetings
Victor.
Okey, i found a way to archive what a was looking for.
I will explain for the sake of those have the same problem before, and hoping to find a experienced user to validate this method... some quiet common is there a multiple ways to do a same thing and as human beings is very difficult to know without proper acknowledge the right path.. so this i a found looking inside the AbstractFormController (that is excellently documented with javadoc).
So what i did was the following, on my controller constructor i add these lines at the end :
setSessionForm(true);
setBindOnNewForm(true);
That all the magic!
But is not enought with setSessionForm(true). According to javadoc the setBindOnNewForm(boolean) method does the following :
/**
* Set if request parameters should be bound to the form object
* in case of a non-submitting request, i.e. a new form.
*/
So my guess are that these two flags are necessary to be marked as true, because :
The setSessionForm makes posible to store as a session attribute the form object, so "is stored in the session to keep the form object instance between requests, instead of creating a new one on each request" (according to javadoc of the setSessionForm method).
The setBindOnNewForm allows the population of the form object with the initial request (despites what type of request method we have). According the javadoc found the AbstractFormController "Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters..."
But still i noticed, following the controller flow with a debugger, that the population is happening inside the method "getErrorsForNewForm(HttpServletRequest request)".. that is where a concrete object of type ServletRequestDataBinder is used IF the setBindOnNewForm is true, and later (as the javadoc stated) the onBindOnNewForm method is invoked, allowing the programmer to overwrite it with custom behavior, the default behavior is just empty (again this was double checked against the code of AbstractFormController).
I have an strong felling to validate my thoughts here, so if anyone can help me, that would be alright, besides the problem is solved!
Thanks to all in advance!
Greetings.
Related
I'm creating a RESTful service with Jersey (2.28) and use Apache Shiro for permission handling. So I used the buildin HttpMethodPermissionFilter which creates permissions like resource:read or resource:write. Now I have the problem that a user may only be allowed to read or write a specific resource and that I would need something like resource:write:<id> or resource:write:<name> or what ever as identifier.
I thought about extending the filter but at that point - even while I could access the body or the url - I have no idea how the data looks like.
Solutions I thought about:
Always pass a query parameter in the url, like /api/resource?id=xxx and if given apply that parameter for the permission string. But there is no way to tell if the parameter is required or not if both resource:read and resource:read:<id> exist. The filter might create a wrong permission for the given url. I could apply the filter only to urls where I know it must be the case, but seems all a bit wonky and error prone.
Remove the filter and ask for the permissions inside of the requested method.
#GET
#Path("/resource/{id}")
public Response getResource(#PathParam("id") String id) {
if(AuthorizationHandler.hasPermission("resource:read:" + id) {
return Response.status(Status.OK).entity("Resource GET works").build();
}
// return 403 or handle exception or ...
}
Somewhat like that, but it will leave me with exception handling in every method which also seems not much preferable. Maybe I could use an ExceptionMapper to handle responses... haven't tried that.
Does maybe someone else have another idea how to solve this efficently or maybe point me to an already existing solution? I'd prefere to use the #RequiresPermissions("resource:read") annotation (or a custom one), but could also define the urls / filters in the shiro.ini file /api/resource/** = noSessionCreation, jwtf, rest[resource] or I fallback to solution 2 if that's recommended.
If I take a service method named public void delete(int id); as the pointcut, I want to add an after-returning advice on it, but I don't know what kind of object was deleted(however, the servlet which called the service knows the type value), so I was wondering if I can pass a customized value to this after-returning advice when it is activated, like 'user'. I've already checked the related document on Spring's website and I still know nothing. I'd appreciate your answer, THX.
One solution but its required refactoring in Service method
1) Refactoring
public class DeleteRequest {
String type;
Long id;
}
public boolean delete(DeleteRequest request){ // impl}
2) Use Around Advice
2.1) Before proceeding method execution, read passed parameter & get to be deleted object for "XYZ" requirement.
2.2) Capture result of delete method execution
IF its TRUE then DO your stuff
Note: I used this approach for deleted entity notification. So how we can get deleted entity information in after-advice, hence keep it entity information in before phase & use it in after-successful execution.
Here's the basic requirement:
An http request is received by page A that may have parameters defined.
If parameters are defined, page A processes the request and automatically forwards to page B. If parameters are not defined, display page A with a form for the user to fill in and submit. On submit, process the request and forward to page B.
Basically, I want to bypass the need for the user to enter data via onSubmit() if the data has already been provided as input parameters to page A. How can I do this?
Page A and B are implemented by extending the (deprecated) SimpleFormController. One way I've done this before is to place a "hidden" page (A′) that accepts the request and, if parameters are defined, processes the data and redirects to B. If the parameters are not provided, then I redirect to A where the processing is done.
This doesn't seem efficient to me, as it duplicates a lot of the processing code.
I'm not providing any code, since it doesn't easily explicate the question.
I hope I understand but you want a method in controller that will direct based on information provided.
Here is something i knocked up quickly that i hope will help solve the problem:
#RequestMapping("/pageA/{parameter}")
public String displayPageA(Model model, #PathVariable String parameter) {
if(parameter == null)
{
model.addAttribute("your_form", new YourForm());
return "pageA";
}
else {
return "redirect:/pageB/"+parameter; // this is if you want the parameter passed on
}
}
I am want to create simple form for searching records via one parameter (for example, name).
Seems like creating a class with one property (name) and than use helpers for forms - is not a best way.
Is there any examles how can I get POST data from request and fetch property value from that data?
Thanks a lot for wasting your time.
You already answered your own question, I just want to provide some more information:
You are right about creating a class with one single property, however keep in mind that you can use validation annotations (like #Required, #Email, etc.) in this class - so if there is some (super) complex logic behind this property this might also be a valuable option.
The second solution would be to use DynamicForms - you use them when you don't really have a model that is backing up the submission form. It goes like this:
public static Result index() {
DynamicForm requestData = Form.form().bindFromRequest();
String name = requestData.get("name");
return ok(name);
}
And of course the third option to get the values is like you mentioned:
String name = request().body().asFormUrlEncoded().get("name")[0];
If you do not what to use form validation, I don't think you need to create a class. Instead, you can use AJAX function like $.ajax(), that will be route to your specific controller function. Moreover, you can call your model function from your controller then at last return the result. The result will be caught by the $.ajax() function.
$.ajax
type: "POST"
url: url
data: data
success: success
dataType: dataType
I am using GWT 2.4 with the editor and request factory frameworks. I have a model, Trip, which has an Address 'origin' and an Address 'destination'. When creating a Trip via the UI, the two addresses are created automatically and assigned to the Trip. User fills out details and saves. For some reason, I am getting the 'autobean frozen error' when trying to persist to the server. This code worked in GWT 2.3 and I cant switch back. I am hoping its not a bug in GWT 2.4. Here is some sample code of what I am doing:
RequestContext request = requestFactory.request();
TripProxy trip = request.create(TripProxy.class);
trip.setOrigin(request.create(AddressProxy.class));
trip.setDestination(request.create(AddressProxy.class));
driver.edit(trip, request);
this.trip = trip;
// … on save button clicked (different method)
RequestContext request = driver.flush();
request.save(trip).with(driver.getPaths()).fire(someReceiverImpl);
Results in:
java.lang.IllegalStateException: The AutoBean has been frozen
at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.checkFrozen(AbstractAutoBean.java:195)
at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.setProperty(AbstractAutoBean.java:270)
at sun.reflect.GeneratedMethodAccessor53.invoke(Unknown Source)
The call to fire completes successfully but somewhere from within requestfactory, the above error is thrown. Curiously, the entity is saved on the server however, validations are not enforced. When I simplify the model and remove the Address associations, the validation and save works. My main issue is the autobean frozen error; the validation stuff is secondary.
EDIT: On further investigation I found that the entities are making it to the server okay and persisting as expected. Its upon return that the above exception is thrown. AddressProxy is a ValueProxy and it looks like RF doesnt like Trip coming back with these associations. Returning null 'fixes' the problem but this obviously wont work long term.
I know this is a lot more than you're asking for, but these 3 tips have helped me out (from here):
Trying to edit locked entity.
If an entity is frozen ( locked for changes) you cannot:
change its properties
use it in requestContext method calls.
If you try to do it, you will receive the exception : java.lang.IllegalStateException: The AutoBean has been frozen.
When entity may be frozen?
every entity returned as a response is frozen
every entity which has been used in requestContext call will be frozen.
In first situation solution is easy – you just have to unlock given entity. In order to do that you must use instance of your RequestContext class and call edit() method.
StudentRequest req1 = requestFactory.studentRequest();
StudentProxy s2 = req1.edit(s1);
In second situation you should not use given entity any more, It cannot be edited because it has already a requestContext assigned. If you want to change it you must retrieve instance of this entity from server again and follow instructions for point a).
Trying to call requestContext.edit() on entity which already has a requestContext assigned.
If you have retrieved the entity from the server or created a new one, and afterwords you are trying to use ANOTHER RequestContext to edit it e.g. in this way:
StudentRequest req = requestFactory.studentRequest();
s1 = req.create(StudentProxy.class);
// s1 is connected with "req" and one context is just enough for it
StudentRequest reqZZZ = requestFactory.studentRequest();
reqZZZ.edit(s1); // you cannot do it - here exception will be thrown
you will surely recieve an exception:
java.lang.IllegalArgumentException: Attempting to edit an EntityProxy previously edited by another RequestContext
You may run into this problem in situations where you have a bean, but you have no track of request context which has created or edited the bean in some previous method call. In this situation you must save the previous requestContext somewhere, or send it along with the entity to the point of interest. The best solution may be to create some special layer which holds currently used request.
Trying to reuse a Request Context which has already been fired.
You can use a request context to create and edit many different entities (also of a different type). You can also accumulate the methods which should be fired. But what you cannot do is to try to use it twice to fire a request. If you have created a request and call the fire() method on it, you cannot do it again. If you do you will get: java.lang.IllegalStateException:A request is already in progress exception.
The solution is to simply create a new requestContext.
This was caused by not using the same EntityManager on the server.