I remain confused by a lot of this stuff. Here is an example:
I have a site that creates pages dynamically based on information in a database. A good example is bio pages. Users add their bios, and then the bios are available through a URL. Now if I use, for example, the user's ID in the URL, thus: members/666 Then it is a simple matter of a lookup in the database and spit out the code. The page is easily bookmarked and returned to. It's even reasonably human readable (members/beelzebub would be even better). I can do some simple things to prevent scripting attacks. I've used this system for many years and have never had a problem. If I use a closure instead I end up with something like this: members/?F92019795619530=_ Can't be bookmarked. Not easily human readable. Expires. For me, this is essentially unworkable. I see the benefit to continuations, but they don't seem like the best solution in every instance. Even in a simple CRUD app, I like being able to bookmark the edit page for an item and come back to it. Where I tend to use continuations is for deletes, or for pages I specifically want to expire. So what is the best practice here, and why? How can I create my dynamic member pages and make them bookmarkable and non-expiring? Is there a Lift way that I'm missing? Chas. marius d. wrote: > > > On Mar 24, 6:53 pm, Alex <[email protected]> wrote: >> Perhaps this debate has been had before but I didn't see it. I have a >> feeling this might be contentious. >> >> Doesn't this violate the basic tenet that you should not use server >> side state when you don't have to? > > No. As long as you are binding a function we are talking about state. > >> There is no reason for instance >> when someone is submitting a news group posting to bind an empty >> posting in a closure and store it on the server, thereby breaking the >> application in any number of cases: >> >> 1) The session times out because the user got distracted or lost their >> connection temporarily >> 2) The user abuses the back button >> 3) The server fails over (assuming there is no memory replication) >> 4) There is a temporary connection failure and the user reloads >> 5) The server is restarted > > I don't see how that would break the application. The function mapping > is cleared is the session times out or it is terminated. Furthermore > Lift has a garbage collection mechanism that removes the functions > that are not utilized. > None of the above cases breaks a Lift application ... if the > application itself is correctly designed and implemented. If we are > talking about clustered applications Lift currently uses sticky > sessions mechanism meaning that all requests pertaining to a given > session are processed by the same node. This can be easily ensured by > load balancing rules. If a node breaks part of the conversation state > can be ensured by application design when processed by a different > node ...of course in the context of a different HttpSession. > >> All of the above cases are recoverable with what, IMO, are properly >> designed frameworks that minimize server-side state. Those are the >> things that taught us state should be stored in the client whenever >> possible. > > Yes and no. I agree with state minimization but adding burden to > client side doesn't solve much in many cases. With Lift and Scala we > are leveraging functions and partial functions to process requests. If > you want that bad purely stateless handling you can also do that with > Lift. Please see LiftRules.statelessDispatchTable. > >> I saw a bit of a discussion distinguishing REST from human >> interaction, saying human interactions are not stateless, but that >> does not excuse the application being so vulnerable to breakage. > > I still don't get where your vulnerability comes from. > >> This reminds me of why I dislike many ASP applications I've used - >> they always break when users don't behave well or when some kicks a >> server - and users, myself included, always misbehave. I use the back >> button when I shouldn't, I leave forms half filled out when I answer >> the phone, and as a frequent traveler I tend to use web applications >> from places with shoddy internet. I also don't like to lose my work >> when a server fails over or is restarted. > > With a bit of care for design this can be taken care. For instance if > you have an Ajax form and the request fails, Lift will try to resend > that ... if the retry request qoes to a new node then we are talking > about the application design if the application has enough contextual > information to handle that or not. Such as in forms that do not > require logged in users (i.e. posting a comment on a blog or forum as > anonymous) the application can safely process the form request in the > context of a new HttpSession/LiftSession. > >> There may be a few gains from designing things the lift way - >> simplicity, security, etc - but they seem to come at too great a cost. > > What is this "great cost" ... one can write poor designed applications > in any language or framework on this Earth (dunno about other > planets). A framework can only do so much but it does not guarantee > flawless applications. > >> There maybe some solutions to this, such as serialization of this data >> to the client, but I'm not sure I like any of them. > > That would be a performance killer. > >> -Alex >> >> On Mar 24, 2:46 am, "marius d." <[email protected]> wrote: >> >>> The foo reference is captured in checkAndSave which is saved on >>> LiftSession in function mapping. >>> Br's, >>> Marius >>> On Mar 24, 10:23 am, Alex <[email protected]> wrote: >>>> If I have this in a (not Stateful) Snippet, is the checkAndSave >>>> closure saving state on the server? Where is the reference to foo >>>> saved? >>>> def add(form: NodeSeq) : NodeSeq = { >>>> val foo = Foo.create >>>> def checkAndSave(): Unit = foo.validate match { >>>> case Nil => foo.save ; S.notice("Started foo: " + foo.body) >>>> case xs => S.error(xs) ; S.mapSnippet("Foo.add", doBind) >>>> } >>>> def doBind(form: NodeSeq) = >>>> bind("foo", form, >>>> "body" -> foo.body.toForm, >>>> "submit" -> submit("Submit", checkAndSave)) >>>> doBind(form) >>>> } > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Lift" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~----------~----~----~----~------~----~------~--~---
