When developing a Web API to interact with Dynamics 365, the model of interaction may be like so:
Plugin performs logic, makes call to Web API (GET, PUT, POST, etc.) -> Web API receives data, performs logic, sends response to Plugin -> Plugin receives response and performs logic with it.
However, if the Web API encounters an error while performing the internal logic, often the plugin will receive an unhelpful error message along the lines of “500 Internal Server Error”. This is due to an error occurring within the internal logic of the Web API. But this by itself is quite unhelpful, and contains no useful information that can be used to debug the issue.
This is particularly the case when used in conjunction with Sandboxed plugins in 365 Online, where you may be using a Web API to perform logic that you cannot accomplish within sandboxed plugins (SQL operations, performing logic from 3rd party libraries, etc.).
Take SQL operations for example. When performing a SQL operation that requires authenticating to a remote SQL database, you may want to know whether you’ve failed to authenticate, and whether that was because of a login error, an error with the database, or some other error. If you lose this error somewhere along the process of it being passed to the plugin, the issue will become a lot more difficult to troubleshoot. So how do you preserve the error messages until the end?
If you know the specific regions of code within the web service that the issues may occur, it is possible to either put try catch blocks around these sections, or check the data received for issues using conditionals before processing. For example, with an ASP.NET API, if the input for the request is not verified, it is possible for a null or incorrectly formatted object to be passed as input to the function. You could then put a check at the top to test the integrity of the data, then if the data is compromised, return an error informing the client that the input was not accepted.
Now that you’re catching specific errors, it’s possible to return more specific errors. When raising errors from your Web API, it’s important to use accurate error codes, so that users who are familiar with http error codes can more easily solve the issue. A list of HTTP Status Codes can be found here:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
When you’ve caught the error, and determined the appropriate status code to represent it, you can raise this error using the HttpResponseException. This will return the error with the specified code to the client.
If you want to add a little more information to your error, you can fill out the body of the response also:
This allows you more direct control over the information transmitted back to the client. This will also make debugging from Dynamics 365 much more efficient. The above code is from a try catch block, where the program is trying to connect to a SQL database. Any errors raised by the connection (invalid password, invalid username, invalid connection url, etc.) are directly inserted into the ReasonPhrase for the Response Message, and returned with that to the client.
Once you have the Web API throwing useful errors, you’ll need to catch and interpret these in your plugin. This is actually fairly simple. Simply place your API operation into a try/catch block, and instead of catching a generic exception, catch a WebException – this is the type of exception returned by the Web API should it encounter any issues. You can then format this exception, and raise it as a plugin exception, which will provide a nice error message for the user. An example of how to do this is given below.
This returns an error similar to the one below.
Which when debugging, is much more useful to the user than a generic Internal Server error. Throwing an InvalidPluginExecutionException also allows you to prevent the plugin from continuing to process with an invalid return from the Web API, however, you could use another method of handling and displaying this error, like tracing it to the plugin trace log. Note that in this example, we are simply raising a Plugin Exception with our formatted error string as input, but it’s also possible to pass an exception object to the InvalidPluginExecutionException, and there are also other parameters you may find useful.