CF11 vs. CF2018 REST 404 Response - rest

I have a REST service that either outputs JSON or returns a 404, depending on whether there's a match. In my existing CF11 setup, I can generate a 404 with
<cfthrow errorcode="404">
but this apparently does not work in CF2018 anymore. In order to generate a 404, I need to use Java (as directed here: How can I send HTTP Status Code and a Response messages to the client in ColdFusion?):
<cfscript>
getPageContext()
.getResponse()
.getResponse()
.sendError( JavaCast( 'int', 404 ), "" );
</cfscript>
This works, but this got me curious, so I created a little test page (not a REST service):
<cfparam name="URL.method" default="cfthrow" >
<cfif URL.method IS "cfthrow">
<cftry>
<cfthrow errorcode="404">
<cfcatch></cfcatch>
</cftry>
</cfif>
<cfif URL.method IS "cfheader">
<cfheader statuscode="404">
</cfif>
<cfif URL.method IS "java">
<cfscript>
getPageContext()
.getResponse()
.getResponse()
.sendError( JavaCast( 'int', 404 ), "" );
</cfscript>
</cfif>
I can't get cfthrow to work here, either. But cfheader DOES work, and as expected, the Java one works, too. Can anyone explain why there are all these disparities? FYI, I'm running Win2012/IIS. Thanks.
p.s. With my existing REST service on CF11, I never could get
<cfheader statuscode="404">
to work, either, to generate a 404. In all cases when I say the 404 doesn't work, what returns is a blank page.

Related

Return multiple data types from a single ColdFusion REST endpoint?

I'm trying to setup a ColdFUsion REST endpoint that can return a file in any number of formats and can return standard error messages but have been running into issues getting that working. For example, a user should be able to send a GET request such as:
https://foo.com/rest/files/123?format=pdf
This would return the file with id 123 in pdf format. And, if the user changed format to txt the same endpoint would return the file in text format. And, if there is no file with id 123, the endpoint would return a 404 error.
The problems I'm running into are twofold:
ColdFusion doesn't seem to give me a way to specify the mime or content type of the file I'm returning and is falling back to whatever is sent in the Accept header sent by the client. This is especially a problem if the client doesn't send that header.
If the client sends an Accept header of application/pdf, I can't get CF to return any error other than a 500 and that's coming from CF, not from me.
The code for my endpoint handler is like this:
<cfcomponent rest="true" restpath="/files/{fileId}" produces="application/json,text/html,application/pdf">
<cffunction name="getFile" access="remote" returnType="any" httpMethod="get">
<!--- query database for file details --->
<cfif qry.RecordCount eq 0>
<cfreturn "404 Not Found" />
<cfelse>
<cfset fileBinary = fileReadBinary(qry.filePath) />
<cfreturn fileBinary />
</cfif>
</cffunction>
</cfcomponent>
Any thoughts? Thanks in advance.

Hash in coldfusion for secure payment gateway

I am trying to create a hash password in coldfusion for our secure payment gateway to accept a transaction.
Unfortunately the payment gateway is refusing to accept my generated hash.
The form sends through all the elements of the transaction and sends a generated hash based on five different fields.
In PHP it is-:
<?php
echo hash('sha256', '
test_site1234
GBP
OrderTotal
OrderID
PASSWORD
');;
?>
Am I right in thinking the code in coldfusion should be -:
<cfset sitesecurity = Hash("test_site1234"&"GBP"&#OrderTotal#&#URL.ThisOrderID#&"PASSWORD", "SHA-256")>
<cfoutput>#sitesecurity#</cfoutput>
I believe the link Miguel-F posted will fix your issue. Coldfusion's hash output is in all uppercase where most (all?) other outputs I've seen are in lowercase. Depending on how your gateway handles case sensitivity you should try passing a lowercase hash.
<cfset sitesecurity = lCase(hash("test_site1234GBP"&OrderTotal&URL.ThisOrderID&"PASSWORD", "SHA-256"))>
The code should have functioned the way it is, but in my opinion it's better to create the value to hash as one big string. Appending to strings is 'costly' because each time you add to a string a new string is created and the old one destroyed. If you're processing one transaction a minute you'd never notice a difference, but it is good practice either way. I would use.
<cfset sitesecurity = Hash("test_site1234GBP#OrderTotal##URL.ThisOrderID#PASSWORD", "SHA-256")>
Now you may have an issue getting a HASH in PHP to match a HASH in ColdFusion, but that's a separate issue.
Sample
<cfset OrderTotal = 10>
<cfset url.ThisOrderID = 50>
<cfset sitesecurity = Hash("test_site1234GBP#OrderTotal##URL.ThisOrderID#PASSWORD", "SHA-256")>
<cfdump var="#sitesecurity#" abort>
Returns
92A14E1D03833CB3FD6932A8E240861CDEC66E46723A544DFBC3C592D5EE7E66

Return object with Zend_Soap

I would like to return an object using Zend_Soap. I also want the object given in the WSDL file. But I can't get both to work together. It's either one or the other.
1. WSDL working, but object not returning
If in the phpdoc of my function on the soap server script, I write
#return Application_Model_Soap_Test
I can see it in the WSDL file
<message name="getPostcodeOut">
<part name="return" type="tns:Application_Model_Soap_Test"/>
</message>
but when I run the script, it returns an empty class:
stdClass Object
(
)
2. Get object, but wrong WSDL
If I change the server function's phpdoc info to
#return mixed Application_Model_Soap_Test
I get an object back with info:
stdClass Object
(
[name] => Fred
[age] => 40
)
but the WSDL file is wrong
<message name="getPostcodeOut">
<part name="return" type="xsd:anyType"/>
</message>
Any ideas? Thanks.
You need to pass a 'classmap' option variable in the client.
See both http://www.php.net/manual/en/soapclient.soapclient.php and http://www.php.net/manual/en/soapclient.soapclient.php about the 'classmap' option passed in the option array.
Basically it should be an array mapping your classes to the classes defined as the returned objects in the WSDL file.
Did you try to turn off caching?
In PHP how can you clear a WSDL cache?
I had the same problem. Every request I made returned me an empty stdclass, even though the WSDL was correct. After disabling caching it worked fine for me. Give it a try.

SOAP Action WSDL

I'm trying to implement a client for National Rail Enquiries' SOAP Service (http://www.livedepartureboards.co.uk/ldbws/).
I stick the WSDL (http://realtime.nationalrail.co.uk/ldbws/wsdl.aspx) into http://soapclient.com/soaptest.html, but I get back the error message "Unable to handle request without a valid action parameter. Please supply a valid soap action."; what on earth should the action be?
Thanks,
Stewart
edit:
I just used soapclient.com as a quick example. In my software, I send the following XML; I still get that I'm missing an action.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://thalesgroup.com/RTTI/2008-02-20/ldb/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:ldbt2="http://thalesgroup.com/RTTI/2008-02-20/ldb/types" xmlns:ldbt="http://thalesgroup.com/RTTI/2007-10-10/ldb/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ct="http://thalesgroup.com/RTTI/2007-10-10/ldb/commontypes" >
<SOAP-ENV:Body>
<ldbt2:GetDepartureBoardRequest xmlns:ldbt2="http://thalesgroup.com/RTTI/2008-02-20/ldb/" >
<ldbt2:numRows>5</ldbt2:numRows>
<ldbt2:crs>WAT</ldbt2:crs>
<ldbt2:filterCrs>GLD</ldbt2:filterCrs>
<ldbt2:filterType>to</ldbt2:filterType>
<ldbt2:timeOffset>0</ldbt2:timeOffset>
</ldbt2:GetDepartureBoardRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
If its a SOAP 1.1 service then you will also need to include a SOAPAction HTTP header field:
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
I have come across exactly the same problem when trying to write a client for the National Rail SOAP service with Perl.
The problem was caused because the Perl module that I'm using 'SOAP::Lite' inserts a '#' in the SOAPAction header ...
SOAPAction: "http://thalesgroup.com/RTTI/2008-02-20/ldb/#GetDepartureBoard"
This is not interpreted correctly by .NET servers. I found this out from Example 3-19 in O'Reilly's Programming Web Services with SOAP . The solution was given below in section 3-20, namely you need to explicitly specify the format of the header with the 'on_action' method.
print SOAP::Lite
-> uri('urn:Example1')
-> on_action(sub{sprintf '%s/%s', #_ })
-> proxy('http://localhost:8080/helloworld/example1.asmx')
-> sayHello($name)
-> result . "\n\n";
My guess is that soapclient.com is using SOAP::Lite behind the scenes and so are hitting the same problem when talking to National Rail.
The solution is to write your own client so that you have control over the format of the SOAPAction header ... but you've probably done that already.
SOAPAction is required in SOAP 1.1 but can be empty ("").
See https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
"The header field value of empty string ("") means that the intent of the SOAP message is provided by the HTTP Request-URI."
Try setting SOAPAction=""
When soapAction is missing in the SOAP 1.2 request (and many clients do not set it, even when it is specified in WSDL), some app servers (eg. jboss) infer the "actual" soapAction from {xsd:import namespace}+{wsdl:operation name}.
So, to make the inferred "actual" soapAction match the expected soapAction, you can set the expected soapAction to {xsd:import namespace}+{wsdl:operation name} in your WS definition (#WebMethod(action=...) for Java EE)
Eg. for a typical Java EE case, this helps (not the Stewart's case, National Rail WS has 'soapAction' set):
#WebMethod(action = "http://packagename.of.your.webservice.class.com/methodName")
If you cannot change the server, you will have to force client to fill soapAction.
I've just spent a while trying to get this to work an have a written a Ruby gem that accesses the API. You can read more on it's project page.
This is working code in Ruby:
require 'savon'
client = Savon::Client.new do
wsdl.document = "http://realtime.nationalrail.co.uk/LDBWS/wsdl.aspx"
end
response = client.request 'http://thalesgroup.com/RTTI/2012-01-13/ldb/GetDepartureBoard' do
namespaces = {
"xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema"
}
soap.xml do |xml|
xml.soap(:Envelope, namespaces) do |xml|
xml.soap(:Header) do |xml|
xml.AccessToken do |xml|
xml.TokenValue('ENTER YOUR TOKEN HERE')
end
end
xml.soap(:Body) do |xml|
xml.GetDepartureBoardRequest(xmlns: "http://thalesgroup.com/RTTI/2012-01-13/ldb/types") do |xml|
xml.numRows(10)
xml.crs("BHM")
xml.filterCrs("BHM")
xml.filterType("to")
end
end
end
end
end
p response.body
Hope that's helpful for someone!
We put together Web Services on Windows Server and were trying to connect with PHP on Apache. We got the same error. The issue ended up being different versions of the Soap client on the different servers. Matching the SOAP versions in the options on both servers solved the issue in our case.
the service have 4 operations:
1. GetServiceDetails
2. GetArrivalBoard
3. GetDepartureBoard
4. GetArrivalDepartureBoard
I have solved this problem, in Java Code, adding:
MimeHeaders headers = message.getMimeHeaders();
headers.addHeader("SOAPAction", endpointURL);

How do I suppress the default apache error document in mod_perl?

I'm developing a RESTful API and I wrote a mod_perl2 handler that takes care of the request.
My handler deals with error codes by setting $r->status($http_code) and return $http_code;
Everything is fine, except a little problem: when my http_code is different than 200 (for instance 404), apache appends a default HTML error document to my own generated response.
For instance:
GET /foo
Gives:
$VAR1 = bless( {
'status' => 404,
'data' => {},
'message' => 'Resource not found for foo'
}, 'My::Response' );
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /foo was not found on this server.</p>
<p>Additionally, a 404 Not Found
error was encountered while trying to use an ErrorDocument to handle the request.</p>
<hr>
<address>Apache/2.0.54 (Fedora) Server at localhost Port 80</address>
</body></html>
How do I get rid of this apache generated HTML?
UPDATE: My fault. My mod_perl2 handler was returning a HTTP_* code instead of Apache2::Const::OK.
I was looking for this too.
And the trick was quite simple:
$r->status(HTTP_NOT_FOUND);
$r->custom_response(404, "");
return OK;
where $r is Apache2::Response object.
See Apache2::Response. I do not have time to experiment right now, but that should work.
Are you asking how to send no message body in your response?
If you want something other than what apache is going to do for you, you need to handle the request yourself. What does the rest of your handler look like? Posting code keeps us from guessing what you are doing.
The return value from your handler lets apache know if you handled the request yourself or if it needs to do something more on your behalf. I'm guessing that you're doing the latter.