I was reading the documentation for cfthrow and came accross this
When to use the cfthrow tag
Use the cfthrow tag when your application can identify and handle
application-specific errors. One typical use for the cfthrow tag is in
implementing custom data validation. The cfthrow tag is also useful
for throwing errors from a custom tag page to the calling page.For example, on a form action page or custom tag used to set a
password, the application can determine whether the password entered
is a minimum length, or contains both letters and number, and throw an
error with a message that indicates the password rule that was broken.
The cfcatch block handles the error and tells the user how to correct
the problem.
Have I been doing it wrong all this time or is this just a terrible use-case?
I was taught that exceptions shouldn’t be used to handle regular application flow but for stuff that is somewhat out of your control. For example, a file being locked when you go to write to it.
A user breaking a password rule doesn’t quite sound like something that’s out of your control.
That is a poor example not a poor use case. I personally would pass in the parameters to a validation function and return a result that contained a pass or fail and a collection of failure messages to display to the user.
How I use exceptions is as follows.
Within functions. Let’s say that you have a function that you are getting some data from the database and you are then constructing a structure from it. If the query returned has no values you have several options:-
You could return an empty structure and let the calling code deduce the problem from the fact the structure is empty. This is not ideal because then the application has to have complicated logic to address the missing data.
You could return a more complex datatype where one property is whether the process went ok and the actual data. Again this is not optimal as you have to then make this access the property on every call when the majority of the time you have data and again your application is dealing with this issue.
Or you could raise a custom exception with cfthrow indicating that there is no record that matches. This then means that you can choose to ignore the prospect of this error happening and let it bubble up to the onError handler or you could surround it in a try catch statement and deal with it there and then. This keeps your API clean and sensible.
Wrapping external errors let’s say that you connect to an external API using cfhttp over https. Now this requires installing the certificate in your keystore otherwise it throws an error. If this certificate gets updated then it will start erroring again. In this instance I would wrap the call in a try catch and should this be the error I would wrap that in my own custom exception with a message detailing that we need to update the cert in the keystore so that any developer debugging it knows what to do to fix it without having to work it out. If it is not that particular error then I would cfrethrow it so that it bubbles up and is dealt with by whatever exception handling logic is above the call.
These are just a few examples, but there are more. To summarise I would say that throwing exceptions is a way of communicating up through the tiers of an application when something has occurred that is not the hoped for behaviour while keeping your API/Application logic clean and understandable.