1

I have a web form which uses the action attribute to call a CFC like this:

<form action="mycfc.cfc?method=registeruser">

The CFC processes the data in the form and then I want it to return a variable telling the user if their form submission has been successful or not.

So within my CFC, I put this:

<cffunction name="registeruser" access="remote" hint="registers a new user" returnformat="JSON">
... PROCESSES FORM HERE THEN...    
<cfset Msg = 'Success'>
<cfreturn Msg>
<cflocation url = "/registrationpage.cfm">
</cffunction>

How do I display the Msg variable in the registrationpage.cfm page? My output is set to JSON so I guess I have to DeSerialize but I have no idea how to actually reference/access this output from the method.

volume one
  • 6,800
  • 13
  • 67
  • 146

2 Answers2

3

My whole answer is for educationnal purposes only and I strongly advise you to use an existing framework rather than reinventing the Wheels. Have a look at Picking a ColdFusion MVC Framework

You can store the value in the session scope. A lot of frameworks does it using a flash memory concept, which is a temporary memory (implemented as a struct) that destroys members when accessed.

Have a look at http://cfwheels.org/docs/1-1/chapter/using-the-flash it's quite straight forward to implement an API that does this.

Client code could look like (depending on your implementation):

<cfset session.flash.set('importantMsg', 'some important msg')>
<cflocation ...>

Then from the other page:

<cfif session.flash.has('importantMsg')>
    <!--- The following line should also destroy the 'importantMsg' key --->
    #session.flash.get('importantMsg')#
</cfif>

Here's an implementation example (not that the implementation is not thread-safe):

FlashMemory.cfc

<cfcomponent>
    <cffunction name="init" returntype="FlashMemory">
        <cfset variables.instance = {flash = {}}>
    </cffunction>

    <cffunction name="set" returntype="void">
        <cfargument name="key" type="string" required="true">
        <cfargument name="value" type="any" required="true">

        <cfset variables.instance.flash[arguments.key] = arguments.value>
    </cffunction>

    <cffunction name="get" returntype="any">
        <cfargument name="key" type="string" required="true">
        <cfset var v = variables.instance.flash[arguments.key]>
        <cfset structDelete(variables.instance.flash, arguments.key)>
        <cfreturn v>
    </cffunction>

    <cffunction name="has" returntype="boolean">
        <cfargument name="key" type="string" required="true">
        <cfreturn structKeyExists(variables.instance.flash, arguments.key)>
    </cffunction>
</cfcomponent>

onSessionStart

<cfset session.flash = new FlashMemory()>

Also please note that in your case, your remote CFC methods shouldn't return anything. You will use the flash memory to pass data around instead. That means when the method has finished it's work you can simply redirect the client.

You probably shouldn't use remote CFC methods in this particular case:

I have never really used remote CFC methods as stateful web services. The various advantages of remote CFC methods like their ability to spit out data in multiple data-interchange formats (JSON, WDDX...) is lost with your implementation.

You could simply do something like:

registration.cfm

<cfset errors = session.flash.get('registrationErrors')>

<cfif arrayLen(errors)>
    <!--- Display errors --->
</cfif>

<form method="post" action="register.cfm">
...
</form>

register.cfm

<cfset registration = new Registration(argumentcollection = form)>
<cfset validator = new RegistrationValidator(registration)>

<cfif validator.validate()>
    <cfset application.userService.register(registration)>

    <!--- You can also redirect to a page that represents the correct state --->
    <cflocation url="registered.cfm" addtoken="no">
<cfelse>
    <!--- Store the errors collection in the flash memory --->
    <cfset session.flash.set('registrationErrors', validator.errors())>

    <!--- Redirect to the page the user came from --->
    <cflocation url="#cgi.http_referer#" addtoken="no">
</cfif>
Community
  • 1
  • 1
plalx
  • 42,889
  • 6
  • 74
  • 90
  • I was thinking to put it in the Session scope, but it somehow felt wrong to sit there. Its just a message. I could of course just add it to the URL as a URL variable but it looks 'messy' having messages in the URL. – volume one Nov 02 '13 at 23:19
  • @volumeone The *flash* pattern makes it quite clean in my opinion and is a widely used approach for that specific problem. – plalx Nov 02 '13 at 23:22
  • I'm not currently using any framework. Was kind of hoping to avoid it if possible. – volume one Nov 02 '13 at 23:33
  • @volumeone You do not need any frameworks to implement this, it's just to show an example of the implementation... – plalx Nov 02 '13 at 23:35
  • @volumeone I added an implementation example ;) – plalx Nov 02 '13 at 23:44
  • 1
    _"Was kind of hoping to avoid it if possible."_ - WHY!? The whole point of established frameworks is to avoid re-inventing common trivialities such as this, and focus on the actual useful bits of an application. – Peter Boughton Nov 02 '13 at 23:47
  • 1
    @PeterBoughton When working with legacy code, that might be too much work to refactor the whole thing using a framework however. – plalx Nov 02 '13 at 23:55
  • 1
    What sort of legacy application doesn't already have its own established methods for submitting forms and displaying messages? Even ignoring that, I disagree entirely; unless an application is being retired, getting a badly written application to at least superficially fit into a framework's structure is a key step in making it maintainable going forward. – Peter Boughton Nov 03 '13 at 00:20
  • 1
    @PeterBoughton Not saying it shouldn't be done, but in a real-world scenario a company might not be able to allocate resources to such refactoring. – plalx Nov 03 '13 at 00:24
  • 1
    Sure, where "real-world" is a euphemism for "badly managed". Smart development teams will invest the relatively small amount of time it takes to impose structure, knowing that it will help reduce maintainance time and effort going forward. But this is all tangental - the questions the OP has been asking are more suggestive of a new build. – Peter Boughton Nov 03 '13 at 00:41
  • The only reason I wanted to avoid using a framework was because it felt like 'cheating'. If I am going to administer the system that I am making, then I thought I should know it inside-out. If an error occurs in the future I felt I should know exactly where it is and why its happening. Rather than it being dealt with in some obscure way that's not visible to me. But my experience with frameworks is zero so I may be totally wrong. – volume one Nov 03 '13 at 11:20
  • @plalx thank you so much for the example. do you advise that using flash memory is better than storing in a Session variable per user then? – volume one Nov 03 '13 at 11:25
  • @volumeone Well in the example I shown you would still create a new `FlashMemory` oject for every sessions. However the big advantage over simply using a normal struct is that the logic that determines the lifetime of the variables that resides in that object is encapsulated. Here I chose to destroy the variables as soon as they are read, so basically once a page access and display a message, the message gets destroyed from the memory, so if the user refreshes the page the message would be gone. *Note that you can store any kind of objects in the FlashMemory object.* – plalx Nov 03 '13 at 13:41
  • 2
    Actually, that is a reasonable concern, but depends on the framework you use (and how long you've been using it). I'm currently working on an app using Coldbox, and there are times when I find myself doing a fair bit of sifting through the Coldbox files to figure out what's actually going on (and getting irrated with bad commit messages). With FW/1 there's far less code to look at, so it's easier to become familiar with and understand. Not sure about cfWheels, but I'd guess it's somewhere in the middle. – Peter Boughton Nov 03 '13 at 13:49
  • 2
    @volumeone Using a framework is not 'cheating' anymore than using jQuery would be. Is using ColdFusion 'cheating' since it is simply and abstraction layer on top of Java? Following that, writing an application in anything but 'machine language' could be considered 'cheating'. Frameworks, and any other kinds of code library exist because the person/people who wrote them had to solve a problem they thought others might have. To not take advantage of these is not only NOT cheating, it is irresponsible, as a developer to NOT use them. – Scott Stroz Nov 03 '13 at 14:29
  • @PeterBoughton I updated my answer and advised the OP that he should be using a framework. – plalx Nov 03 '13 at 14:30
2

Take the cflocation tag out of your function. It will not execute anyway because it's after the cfreturn tag.

Also, post your form to a .cfm page, not the .cfc. From that .cfm page, do this:

<cfset MyObject = CreateObject(stuff for your cfc goes here)>
<cfset MessageFromFunction = MyObject.registeruser()>
<cfoutput>#MessageFromFunction</cfoutput.
Dan Bracuk
  • 20,699
  • 4
  • 26
  • 43
  • This will be mixing logic and presentation in one CFM file? I was trying to keep them separate... – volume one Nov 02 '13 at 23:32
  • 2
    Your login logic is in the cfc, not the cfm. Besides, mixing logic and presentation in the same cfm file is not exactly bad. – Dan Bracuk Nov 03 '13 at 01:58
  • @DanBracuk Mixing presentation and logic is extremely bad, unless you like spaghetti code. That's one of the reasons MVC exists. – plalx Nov 03 '13 at 13:45
  • This example does not mix presentation and logic, nor would I say it would qualify as 'spaghetti' code. The best solution is for you to use a community supported framework. Some very smart people have already solved this problem, But you are trying to re-invent the wheel. Dan's solution is more elegant than you trying to submit a form to a CFC and doing a `cflocation` (which shows that you know very little about how CFCs work). When you ask for help, then complain about the responses, makes people not want to help you. – Scott Stroz Nov 03 '13 at 14:21
  • @ScottStroz Yes it does mix presentation and logic. See my answer for a solution that wouldn't. This answer is missing a layer... – plalx Nov 03 '13 at 14:35
  • 1
    It's _not_ bad to "mix presentation and logic" - it's an essential requirement for writing many applications. Of course, if you mix _business_ logic with _display_ logic, you can easily end up with a mess. – Peter Boughton Nov 03 '13 at 14:43
  • 1
    This answer doesn't have the business logic (that's in the CFC), however it *is* mixing controller logic (what happens - i.e. the registeruser call) with the view layer (what is displayed - the message). Not ideal, but probably not worth worrying about for a simple example like this. – Peter Boughton Nov 03 '13 at 14:43
  • Without a _real_ controller, there will ALWAYS be some kind of mix of logic and presentation (but not necessarily 'business logic' as Peter pointed out). – Scott Stroz Nov 03 '13 at 14:45
  • @ScottStroz Exactly what I was saying, this answer it missing a layer ;) – plalx Nov 03 '13 at 14:51
  • Your answer is as well – Scott Stroz Nov 03 '13 at 16:34
  • @ScottStroz The **big distinction** in my answer is that there's **only presentation code in the `registration.cfm` file**. The *controller*'s code is encapsulated in `register.cfm` and the BL in CFCs (like in this answer). The `register.cfm` file is acting as an application service while the `UserService` component could be considered a domain service (in DDD). With my design you could have multiple views reusing the same service, while in this current example you can't without duplicating the *controller/application service*'s logic because it's mixed with the presentation's code. – plalx Nov 03 '13 at 17:36
  • @PeterBoughton Yeah, I meant mixing BL with presentation. I thought it was implicit in my statement. I should have been more precise. – plalx Nov 03 '13 at 17:42
  • I knew what you meant, but I've also seen enough ugly ways people have contrived to avoid having any logic at the view layer, obviously due to misunderstanding similar statements, so feel it is something worth clarifying. – Peter Boughton Nov 03 '13 at 18:38