So I'm very new to SOAP and I am trying to connect to the National Weather Service's SOAP service in order to pull forecast data to display on my webpage. Here is my short code for this process:
<cfinvoke
webservice="http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl"
method="NDFDgen"
returnvariable="aTemp">
<cfinvokeargument name="latitude" value="37.94"></cfinvokeargument>
<cfinvokeargument name="longitude" value="-75.47"></cfinvokeargument>
<cfinvokeargument name="product" value='"glance"'></cfinvokeargument>
<cfinvokeargument name="startTime" value="2014-05-02T12:00"></cfinvokeargument>
<cfinvokeargument name="endTime" value="2014-05-05T12:00"></cfinvokeargument>
<cfinvokeargument name="Unit" value='"e"'></cfinvokeargument>
<cfinvokeargument name="weatherParameters" value="maxt = TRUE"></cfinvokeargument>
</cfinvoke>
The problem is, when I try and run my webpage, I'm getting the following error:
Web service operation NDFDgen with parameters {Unit={"e"},startTime={2014-05-02T12:00},endTime={2014-05-05T12:00},product={"glance"},longitude={-75.47},weatherParameters={maxt = TRUE},latitude={37.94}} cannot be found.
I'm a bit confused since the NDFDgen operation does indeed exist in the WSDL file I am retrieving, and I have addressed all of the required parameters for the NDFDgen operation.
Link to the WSDL file I am trying to use:
http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl
Link to the functions page with required parameters:
http://graphical.weather.gov/xml/#use_it
Can anyone see anything wrong with my code? Is it perhaps something with my arguments I'm passing to SOAP? I even tried following the advice of this Stack Overflow question (Consuming ColdFusion webservice - Web service operation with parameters {} cannot be found) and added the
refreshwsdl="yes"
attribute to my <cfinvoke>, but I am still getting the same error.
With complex web services, it is often easier to go the xml + cfhttp route, as Chester suggested. However, to answer your question, there are a few things wrong with the arguments. That is what the error message means. A method by that name may exist, but its signature does not match the values you have supplied. There is either mismatch in the number of arguments, or in this case, the type of arguments.
According to the wsdl, the start/endTime values must be dates. While CF can implicitly convert a variety of U.S. date strings, it cannot parse the format you are using: yyyy-MM-ddThh:mm. So either use date objects, or use "parseable", date strings such as yyyy-MM-dd hh:mm:ss.
The "weatherParameters" argument should be structure (or complex type) not a string:
<cfset weather = {maxt=true}>
...
<cfinvokeargument name="weatherParameters" value="#weather#">
The Product and Unit values have too many quotes. By using value='"glance"' you are actually including the double quotes as part of the value. That will probably cause an error because the remote web service expects to receive glance (no quotes).
While it will not cause an error, you do not need to include closing tags: </cfinvokeargument>. If you prefer to close it, it is cleaner to use the shortcut <cfinvokeargument ... />
With those changes, your call should work as expected. Though you might want to consider switching to createObject, rather than cfinvoke. Then you can dump the web service object for debugging purposes. It is also less bulky IMO.
<cfscript>
ws = createObject("webservice", "http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl");
//show web service methods for debugging purposes
writeDump(ws);
// construct arguments
args = {latitude="37.94"
, longitude="-75.47"
, product="glance"
, startTime="2014-05-02 12:00:00"
, endTime="2014-05-05 12:00:00"
, Unit="e"
, weatherParameters={maxt=true}
};
// call the method
result = ws.NDFDgen(argumentCollection=args);
writeDump(result)
</cfscript>
Related
I am migrating an application from ColdFusion 9 to ColdFusion 11.
In the existing code there is an assignment of a variable to false:
<cfset VARIABLES.roleTypeId = false >
And then, farther down, a function that expects this variable to be numeric:
<cffunction name="rolesForStudy" >
<cfargument name="id" hint="Study Id">
<cfargument name="roleTypeId" default="#VARIABLES.roleTypeId#" type="numeric"/>
</cffunction>
I inherited the code, and I cannot defend the original programmer's decision to set it up this way -- but, in short, it worked in ColdFusion 9, and it doesn't work in ColdFusion 11 (returning a data type error). I assume that ColdFusion 9 was automatically converting false to 0.
My question: Is there a configuration setting that I can change in ColdFusion 11 to make it do the conversion like ColdFusion 9 did? Or will I have to fix this code, along with probably lots of other similar examples throughout the application? Neither I nor our ColdFusion administrator has been able to find any information about this in the ColdFusion Administrator interface, the ColdFusion documentation, or online.
Edit in Response to Adam Cameron in comments
I have created a file that consists of the following 10 lines (and nothing else):
<cfset VARIABLES.roleTypeId = false >
<cfoutput>
<p>#rolesForStudy( 1, VARIABLES.roleTypeId )#</p>
</cfoutput>
<cffunction name="rolesForStudy" >
<cfargument name="id" hint="Study Id">
<cfargument name="roleTypeId" default="#VARIABLES.roleTypeId#" type="numeric"/>
<cfreturn "It worked" >
</cffunction>
When I execute it in ColdFusion 9, it displays the words "It worked".
when I execute it in ColdFusion 11, it returns the following error message:
If the component name is specified as a type of this argument, it is possible that either a definition file for the component cannot be found or is not accessible.
I believe you will have to fix the code. There are no settings (that I know of at any rate) that alter the way CF handles boolean types. You should be able to change the assignement above from "false" to 0 and your function code will work. However I suspect elsewhere you might have something like <cfif variables.roletypeID IS "False"> which will then be broken as it is in truth looking for a string - which also works (ha). CF's handling of a myriad of values as boolean (0 or not 0, true, false, yes and no) is a legacy of it's origin. It's convenient at times but definitely leads to things like this.
Meanwhile I wonder if this change of behavior is a new bug or the fixing of an old bug. In my view passing "false" as an argument and having it read as numeric seems inconsistent so the new way of doing it seems right to me. However, many many languages treat 0 or not 0 as default values for true and false.
EDIT:
According to Adam's comments below, my example of code where someone would say:
<cfif somevar IS "false">
...would work even if somevar was indeed numeric. His example (useful) is that:
#0 is "False"#
...will output "yes" - so CF is recasting the string "false" to a zero under the hood for comparison. That makes my example incorrect.
My answer is still correct I believe. The issue he's running into is that the argument passed to his function - being of the type "boolean" is throwing an error because the function expects a numeric value. But Adam's point and example makes me think perhaps this behavior is a bug - since it appears CF is not casting to a number before checkign the type (something it did do in CF 9 according to the Joe).
NumberFormat is your saviour, credit going to this comment on one of Ben Nadel's articles.
<cfset VARIABLES.roleTypeId = false>
#NumberFormat(VARIABLES.roleTypeId)# <!-- = 0 -->
<cfset VARIABLES.roleTypeId = true>
#NumberFormat(VARIABLES.roleTypeId)# <!-- = 1 -->
So you should be able to either convert it to the expected numeric type before calling the function, or just doing
<cfargument name="roleTypeId" default="#NumberFormat(VARIABLES.roleTypeId)#" type="numeric">
I've raised this as a bug in ColdFusion 11: "Type coercion failure when passing boolean to a numeric arg".
I recommend you work around this by taking the type check off the argument. This is probably the lowest-impact fix.
This is also a rare case in which I'd add a comment to code, explaining why you've taken the type checking off.
Apologies to Joe, Mark & Duncan for emphatically contradicting what they were finding. That said, I don't agree that their answers are the best approach here ;-)
After my form submits I am calling a controller method that runs an orm EntitySave in my cfc. I would like to dump out the arguments before I save my data via ORM just to visually validate those are indeed the values I want to save in the database.
So when I use this
<cfthrow message="value = #arguments#">
I am getting this:
Error: Complex object types cannot be converted to simple values.
I understand you are not allowed to do this with complex objects, so in those cases I would use <cfdump> but I can't find a way to dump in a <cfthrow>. I am sure there is a better way to accomplish this. I have also tried doing a <cfmail> to myself which works amazingly but the email will take a minute or two. Any suggestions would be greatly appreciated. I am currently checking into ValidateThis.
You could serialise it:
<cfthrow message="value = #serializeJson(arguments)#">
But I don't think you want that sort of thing showing up on the screen.
I'd log it if I was you (so same notion, just <cflog> before the <cfthrow>, and put the arguments in the log entry, and in the <cfthrow> just put a brief explanation of the error (you should also use a TYPE attribute, for subsequent handling of the exception that you've raised.
Rather than throwing it, you could try dumping it to a file and see if that meets your needs:
<cfdump var="#arguments#" output="C:\dump.html" format="html">
If you need to abort (as a throw would do), you can add abort on to the end of the tag, <cfdump... abort>.
You could do the following in order to use <cfdump>:
<cfsavecontent variable="arguments_dump">
<cfdump var="#arguments#" />
</cfsavecontent>
<cfthrow message="#arguments_dump#" />
Takes a little bit more code however and is not as elegant as Adam Cameron's answer above.
I want to do something in ColdFusion that is similar to sprintf in C or Perl. I found this
answer, which seems to be what I'm looking for. However, I can't get it to work.
Here is what I'm trying:
<cftry>
<cfset firstName="John">
<cfset output=createObject("java","java.lang.String").format("Hello, %s!", firstName)>
<cfcatch type="any">
<cfdump var="#cfcatch#" expand="false">
</cfcatch>
<cftry>
And here is what I get:
cfcatch.Message: The format method was not found.
cfcatch.Detail: Either there are no methods with the specified method
name and argument types or the format method is overloaded with
argument types that ColdFusion cannot decipher reliably. ColdFusion
found 0 methods that match the provided arguments. If this is a Java
object and you verified that the method exists, use the javacast
function to reduce ambiguity.
This is an overloaded method, so I did as suggested and used JavaCast on the arguments:
<cfset output=createObject("java","java.lang.String").format(JavaCast('string', "Hello, %s!"), firstName)>
<cfset output=createObject("java","java.lang.String").format("Hello, %s!", JavaCast('string', firstName))>
<cfset output=createObject("java","java.lang.String").format(JavaCast('string', "Hello, %s!"), JavaCast('string', firstName))>
and got the same error every time.
I tried another static method on the String class, valueOf, and it worked fine.
Edit:
I've already seen a comment, and I'm not sure how to respond to those, but maybe I should explain here. What I've shown above is an extremely simplified example of what I am trying to do. The goal is to use a format string to provide lots of formatting in one place, and then simply pass in a list of variables, instead of formatting a bunch of variables and outputting them, or formatting them as I'm outputting them. With the format method, I plan to build a set of format strings that match the output I need, then I will just cfloop or cfoutput over a query, run this one method inside, and get the output I want. No DateFormat, NumberFormat, Left, Right, etc. If I can't get this working, that is plan B though.
I'm running ColdFusion 9.01, Windows 7, Java 1.6.0_22.
Any and all help is greatly appreciated.
Answer based on my comment above:
It probably can't match the format method you're looking for because its signature is format(String, Object[]) and your second String argument can't be automatically converted to an Object array.
You could change the argument to encapsulate the name in an array as follows: format("Hello, %s!", [firstName]).
Thanks to Leigh for being so courteous, I upvoted your comments :)
I have been tasked with researching the possibility of storing .cfm files outside of the web root as a further security measure. (I'm not talking about CFCs, but the files that will appear in the URL string.)
I figured that I could use the properties of OnRequest() in the Application.cfc to do this, but no luck.
I've tried this:
<cffunction name="OnRequest" access="public" returntype="void" output="true">
<cfargument name="TargetPage" type="string" required="true"/>
<cfinclude template="C:/test#arguments.TargetPage#"/>
<cfreturn/>
</cffunction>
I then put a test.cfm inside C:\test and attempted http://localhost/test.cfm. That gives me a "file not found" error.
So then I created a ColdFusion mapping to C:\test called "test" and changed my CFINCLUDE thusly:
<cfinclude template="/test#arguments.TargetPage#"/>
Unfortunately I got the same error.
So, is what I'm after even possible? (Windows Server 2008 and IIS in case it matters. ColdFusion 9.)
I know that I could put a CFINCLUDE in my index.cfm and pass around URLs like http://www.example.com/?file=test.cfm, but I'd prefer not to do that.
Well here's something: If I put an empty test.cfm in my web root then this works. Obviously I'd really rather not put empty stubs for every one of my files (which, for this project, will end up being in the hundreds).
Rather than spinning your wheels down this path, let me point you in a possible better direction which may solve the original issue, but from a more preferred/best practices standpoint:
ColdFusion Sourceless Deployment
Rather than trying to jimmy-rig the site to read files outside of root, you simply never put the source code out into the world.
I think you would have better luck using onMissingTemplate rather than onRequest. http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7d2a.html
I am working on redoing our company's code, and I want to have a clear, easy to read, and reasonably secure application.cfm.
And no, we are not using application.cfc. So let's not discuss that please.
Just want to know what scripts you would add for security.
I am using coldfusion 8 standard, sql 2008.
Here is one of the scripts I am currently using, but I want to hear from some other coldfusion programmers.
<cfset temp = cleanScopes('form,url') />
<!--- another method to clean url/form data from http://www.garyrgilbert.com/tools/coldfusion/cleanScopes.cfm.txt --->
<cffunction name="cleanScopes" access="public" returntype="void">
<cfargument name="scopesToClean" type="string" required="yes">
<cfargument name="charlist" type="string" required="no" default="">
<cfscript>
reTags ="<[^/>]*>|</.*>";
</cfscript>
<cfloop list="#scopestoClean#" index="scopeName">
<cfif not findnocase("multipart/form-data",cgi.CONTENT_TYPE)>
<cfscript>
s=Evaluate(scopeName);
for(field in s)
if (isSimpleValue(s[field])){
if(reTags neq '')
do { prev=s[field];
s[field]=REReplaceNoCase(s[field],reTags,"","ALL");
} while (prev NEQ s[field]);
structUpdate(s,field,prev);
if (charlist neq '')
s[field] = replacelist(s[field],charlist,'');
}
</cfscript>
</cfif>
</cfloop>
<cfreturn>
</cffunction>
Thank you for your time.
I would advise against attempting to catch everything in a global fashion. There will inevitably be a few things that slip through the cracks, no matter how complex and convoluted your global protection code gets.
Instead, the "correct" (for what it's worth) method is to sanitize all content being presented on a page (or in an email, etc) -- during output -- that began its life as user input.
That said, take a look at OWASP. They have excellent libraries for protecting from all kinds of attacks, including the various ones you mention (sqli, xss, crlf). A coworker of mine recently wrapped up some of those libraries into a CFC that we can use in our applications, and explained how to use it on our developers blog:
AntiSamy
If your application accepts user generated HTML, say blog comments for example, you need to make sure you sanitize your input to prevent XSS attacks. You wouldn’t want someone to be able to enter malicious code in your blog comments so you need some way to filter the input. Enter AntiSamy. AntiSamy allows you to easily filter user generated HTML according to what it terms policies. AntiSamy is a Java project, so I have packaged it into a CFC for easy use from ColdFusion.
The simplist way to use AntiSamy is to create an instance of the AntiSamy component (cfc.owasp.AntiSamy) and call the getCleanHTML() method on the input.
<cfset antisamy = CreateObject("component","cfc.owasp.antisamy") />
<cfset cleanHTML = antisamy.scan(form.someInput) />
This will run AntiSamy with the default (fairly permissive) policy file and return the clean HTML markup.
ESAPI Encoder
The next library I’ve brought over from the OWASP project is the ESAPI Encoder. Again this is a Java project which I have wrapped in a CFC for easier use. The encoder provides several methods for encoding beyond those included with ColdFusion. Some of the more useful methods include encodeForJavaScript(), encodeForHTMLAttribute(), and encodeForCSS(). Using the component is pretty straight forward, just instantiate it and call the appropriate method.
<cfset encoder = CreateObject("component","cfc.owasp.Encoder") />
<cfset html = encoder.encodeForHTML("<body onload=""alert('XSS')"">Test</body>") />
One very useful method this library provides is the canonicalize method. The documentation from the beta version of the ESAPI Encoder gives a good description of what this method does.
However, if you insist on a global solution, why reinvent the wheel? Why not try out something like FuseGuard. The price is probably less than the cost of the development-hours that would be spent cobbling together, debugging, and dealing with security problems that break through your home-grown system.
Personally, I'm not really sure this "global" approach is the best. I check all incoming data in all models that accept external data, with specific validation rules for each situation. So additional layer looks overkill.
Such scripts wont protect you from putting string into the numeric id passed into the URL -- you have to check it any way. You have to use HTMLEditFormat/XMLFormat in the views any way, and so on.
P.S. List loop for CFScript:
for (i=1; i LTE ListLen(scopestoClean); i++) {
scopeName = ListGetAt(scopestoClean,i);
//... following code
}