{"id":8621,"date":"2015-01-15T06:00:00","date_gmt":"2015-01-15T14:00:00","guid":{"rendered":"http:\/\/www.sapien.com\/blog\/?p=8621"},"modified":"2016-12-15T15:09:16","modified_gmt":"2016-12-15T23:09:16","slug":"manage-errors-in-a-gui-application","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2015\/01\/15\/manage-errors-in-a-gui-application\/","title":{"rendered":"Manage Errors in a GUI Application"},"content":{"rendered":"<p><a class=\"twitter-follow-button\" href=\"https:\/\/twitter.com\/juneb_get_help\" data-lang=\"en\">Follow @juneb_get_help<\/a><\/p>\n<p>In <a href=\"http:\/\/www.sapien.com\/blog\/2014\/12\/15\/display-output-in-a-gui-application-copy\/\" target=\"_blank\">a previous post<\/a>, I talked about displaying output in a GUI application. But I really covered only the primary output of cmdlets. In this post, we&#8217;ll discuss some very basic strategies for detecting and displaying terminating and non-terminating errors in a GUI application.<\/p>\n<p>This is a critical topic. If you&#8217;re used to scripting in the console, errors are displayed automatically. But, in a GUI program, you manage the output &#8212; all of it. And nothing appears in your application window unless you place it there explicitly.<\/p>\n<p>Your application&#8217;s response to an error can be as varied as your imagination &#8212; from a simple &#8220;Error&#8221; message box to changing the entire form, changing colors, and popping up help messages. But, in this post, we&#8217;ll show you the basics.<\/p>\n<p>&nbsp;<\/p>\n<h2>Errors Do Not Appear Automatically<\/h2>\n<p>In the Windows PowerShell console, both terminating and non-terminating errors appear in the all-too-familiar red text.<\/p>\n<pre class=\"output\">PS C:\\ps-test&gt; 1\/0\r\n<span style=\"color: red;\">Attempted to divide by zero.\r\nAt line:1 char:1\r\n+ 1\/0\r\n+ ~~~\r\n+ CategoryInfo : NotSpecified: (:) [], RuntimeException\r\n+ FullyQualifiedErrorId : RuntimeException<\/span><\/pre>\n<p>In PowerShell Studio, when you run a command that generates an error &#8230;<\/p>\n<pre lang=\"PowerShell\">$buttonStart_Click = {\r\n    $textboxOutput.Text = 1\/0\r\n}<\/pre>\n<p>&#8230; the error appears in the Output pane.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/image10.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"image\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/image_thumb10.png\" alt=\"image\" width=\"615\" height=\"243\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>But, when you package the .psf file into an executable file (.exe) and run it, <b>NOTHING<\/b> appears in the GUI window. There is no standard output to display. And, this code doesn&#8217;t capture or redirect the error output to any control in the UI.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image003.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image003\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image003_thumb.png\" alt=\"clip_image003\" width=\"462\" height=\"406\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>This result is confusing to users who are expecting feedback from the action of clicking the button.<\/p>\n<p>Let&#8217;s fix this.<\/p>\n<p>&nbsp;<\/p>\n<h2>Errors do not stop a GUI application<\/h2>\n<p>Windows PowerShell supports two distinct types of errors. <i>Terminating errors<\/i> stop the pipeline. <i>Non-terminating<\/i> errors let the pipeline continue.<\/p>\n<p>But neither terminating nor non-terminating errors stop a GUI application. The application continues to run. You can respond to any error by closing the application, but to do so, you must explicitly close the form. It doesn&#8217;t happen automatically.<\/p>\n<p>Similarly, you can also respond to any error, including a terminating error, by reinitializing your input controls and letting the user try again. The choice is yours, but you need to consider the options and implement your response.<\/p>\n<p>&nbsp;<\/p>\n<h2>Display errors in the output window<\/h2>\n<p>The simplest way to notify a user of an error is to display an error in the text box that is designed for standard output.<\/p>\n<p>This example uses a <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847793.aspx\"><b>Try-Catch<\/b><\/a> block to catch terminating errors in a <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113309\"><b>Get-Command<\/b><\/a> command. This sample code displays an error message when the user types an empty string.<\/p>\n<pre lang=\"PowerShell\">$buttonGetCommand_Click = {\r\n    try\r\n    {\r\n        $cmdlet = textboxInput.Text\r\n        $textboxOutput.Text = Get-Command -Name $cmdlet | \r\n            Format-Table -AutoSize | \r\n            Out-String\r\n    }\r\n    catch\r\n    {\r\n        $textboxOutput.Text = \"Enter a cmdlet name. `\r\n            The name cannot be an empty string.\"\r\n    }\r\n}<\/pre>\n<p>Here is the result.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image004.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image004\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image004_thumb.png\" alt=\"clip_image004\" width=\"456\" height=\"400\" border=\"0\" \/><\/a><\/p>\n<p>Be sure to fix the code to avoid obvious terminating errors, like this $null value error, but include a <b>Try-Catch<\/b> block (preferred) or <b>Trap<\/b> statement (outdated, but still available) to handle the ones that you didn&#8217;t anticipate.<\/p>\n<p>&nbsp;<\/p>\n<h2>Display errors in red text<\/h2>\n<p>When you use the same output control for standard and error output, be sure to make errors immediately recognizable by displaying them in red text. Use the <b>ForeColor<\/b> property of a <b>Textbox<\/b> with a value of &#8216;Red&#8217;.<\/p>\n<p>When the output is read-only, use a <b>RichTextBox<\/b> control, instead of a <b>TextBox<\/b>. When you set the <b>ReadOnly<\/b> property of a <b>Textbox<\/b> to $true, the <b>ForeColor<\/b> property is overridden by <b>ReadOnly<\/b> default colors. The <b>RichTextBox<\/b> control implements the <b>ForeColor<\/b> property value even when <b>ReadOnly<\/b> is $true.<\/p>\n<p>This sample code displays the contents of a file. The user types the path and file name in the input text box ($textboxInput) and the script displays the file content in a rich text box ($richTextBoxOutput).<\/p>\n<pre lang=\"PowerShell\">$buttonGetContent_Click = {\r\n    $this.Enabled = $false\r\n    try\r\n    {\r\n        if (!($file = $textboxInput.Text.Trim()))\r\n        {\r\n            Display-Error \"File name cannot be blank. Enter a path and file name.\"\r\n        }\r\n        else if (!(Resolve-Path $file))\r\n        {\r\n            Display-Error `\r\n                -ErrorString \"Can't find $($Error[0].CategoryInfo.TargetName). `\r\n                Check the path and file name and try again.\"\r\n        }\r\n        elseif (!($richtextboxOutput.Text = `\r\n            Get-Content -Path $file -ErrorVariable fileErr | \r\n            Out-String))\r\n        {\r\n            Display-Error -ErrorString \"Can't get content of $($file). `\r\n                `nError is: $($fileErr).\"\r\n        }\r\n    }\r\n    catch\r\n    {\r\n        Display-Error -ErrorString \"Unforeseen error. Please try again. `\r\n            `nIf the problem recurs, contact juneb@sapien.com.\"\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>The code assigns the standard output to the <b>Text<\/b> method of the rich text box. To display each value on a separate line, use the <b>Raw<\/b> parameter of the <b><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113310\">Get-Content<\/a> <\/b>cmdlet, which returns a single string, instead of an array, or the <b><a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?linkid=293999\">Out-String<\/a><\/b> cmdlet, which converts the output to a single string.<\/p>\n<pre lang=\"PowerShell\">$richtextboxOutput.Text = Get-Content -Path $file -ErrorVariable fileErr -Raw<\/pre>\n<p>-or-<\/p>\n<pre lang=\"PowerShell\">$richtextboxOutput.Text = Get-Content -Path $file -ErrorVariable fileErr | Out-String<\/pre>\n<p>&nbsp;<\/p>\n<p>Here&#8217;s the standard output:<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image005.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image005\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image005_thumb.png\" alt=\"clip_image005\" width=\"462\" height=\"267\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>To display any error, the script calls the <b>Display-Error<\/b> function, which sets the <b>ForeColor<\/b> property of the rich text box to red, assigns the error message to the rich text box&#8217;s <b>Text<\/b> property, and places the cursor in the input text box, which signals the user to try again.<\/p>\n<pre lang=\"PowerShell\">function Display-Error\r\n{\r\n    param\r\n    (\r\n        [parameter(Mandatory = $true)]\r\n        [String]\r\n        $ErrorString\r\n    )\r\n    \r\n    $richtextboxOutput.ForeColor = 'Red'\r\n    $richtextboxOutput.Text = $ErrorString\r\n    $textboxInput.Focus()\r\n}<\/pre>\n<p>Here&#8217;s the result.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image006.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image006\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image006_thumb.png\" alt=\"clip_image006\" width=\"472\" height=\"273\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Unfortunately, if you try to restore the <b>ForeColor<\/b> in the same function, it&#8217;s restored immediately and you never see the red text. I could have used the SelectionColor property, but, instead, I restored the standard text color in the <b>TextChanged<\/b> event of the input text box.<\/p>\n<p>So, when the user tries again, the output is cleared, the button is re-enabled, and the original text color is restored.<\/p>\n<pre lang=\"PowerShell\">$textboxInput_TextChanged={\r\n    $richtextboxOutput.Clear()\r\n\r\n    if ($textboxInput.Text.Trim() -ne \"\")\r\n    {\r\n        $buttonGetContent.Enabled = $true\r\n        $richtextboxOutput.ForeColor = 'WindowText'\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Display raw errors<\/h2>\n<p>Displaying the error message that Windows PowerShell returns is considered to be bad practice. The error messages in a well-designed application should be clear, well-written, and customized for the controls in the interface.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image007.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image007\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image007_thumb.png\" alt=\"clip_image007\" width=\"473\" height=\"289\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>However, you might want to include some elements of the raw error message in your error message display.<\/p>\n<p>For example, the error message in this code includes the fully-qualified path that the cmdlet actually searched ($error[0].CategoryInfo.TargetName). This is really important when the user enters a file name without a path and misjudges the current directory. It&#8217;s so useful that I substitute <a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?linkid=293904\">Resolve-Path<\/a>, which returns this error, for the more traditional <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113418\">Test-Path<\/a>, which returns a Boolean value ($True\/$False).<\/p>\n<p>To display the error message to the user, the code assigns the error message to the value of the <b>Text<\/b> property of the <b>RichTextBox<\/b>.<\/p>\n<pre lang=\"PowerShell\">if (!(Resolve-Path $file))\r\n{\r\n    $richtextbox.ForeColor = 'Red'\r\n    $textboxInput.Focus()\r\n    $richtextboxOutput.Text = \"Can't find $($Error[0].CategoryInfo.TargetName). `\r\n        Check the path and file name and try again.\"\r\n}<\/pre>\n<p>Here&#8217;s the result.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image008.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image008\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image008_thumb.png\" alt=\"clip_image008\" width=\"505\" height=\"293\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>Display errors in an error window<\/h2>\n<p>You can also display errors in an error window. This occupies a lot of real estate, but might occasionally be a useful element.<\/p>\n<p>This code assigns standard output to the Text method of $richtextBoxOutput, as in the previous example.<\/p>\n<pre lang=\"PowerShell\">$richtextboxOutput.Text = Get-Content -Path $file -ErrorVariable fileErr | Out-String<\/pre>\n<p>But, it assigns errors to the Text method of $richtextBoxError.<\/p>\n<pre lang=\"PowerShell\">if (!($file = $textboxInput.Text.Trim())\r\n{\r\n    $richtextboxError.Text = \"File name cannot be blank. Enter a path and file name.\"\r\n}<\/pre>\n<p>Because the $richtextboxError is a dedicated error display, you can set its <b>ForeColor<\/b> property to &#8216;Red&#8217; in the Properties dialog and never change it, sot here&#8217;s no need for a separate DisplayError function.<br \/>\nHere&#8217;s the result when the command succeeds.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image009.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image009\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image009_thumb.png\" alt=\"clip_image009\" width=\"414\" height=\"361\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>And here&#8217;s the result when it fails.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image010.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image010\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image010_thumb.png\" alt=\"clip_image010\" width=\"413\" height=\"360\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>You might be tempted to make the error box invisible until you use it ($richtextboxError.Visible=$false), but invisible text boxes still occupy space. And the result is, um, disconcerting. Don&#8217;t do it.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image011.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image011\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image011_thumb.png\" alt=\"clip_image011\" width=\"420\" height=\"366\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>Display errors in a message box<\/h2>\n<p style=\"padding-left: 30px;\"><em>For detailed help with message box variations, see Spotlight on the MessageBox Control.<\/em><\/p>\n<p>You can also display errors in a message box. This is quick and easier than maintaining a separate text box.<\/p>\n<p>A message box isn&#8217;t a control. You create it on the fly and, by default, it includes an OK button. You don&#8217;t need to create it before you use it and it disappears automatically when the user clicks OK.<\/p>\n<p>For example, if the <a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?linkid=293904\" target=\"_blank\">Resolve-Path<\/a> cmdlet can&#8217;t find the specified file, a message box pops up with the error message. Then,\u00a0the code sets the focus back to the input text box.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image012.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image012\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image012_thumb.png\" alt=\"clip_image012\" width=\"504\" height=\"260\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>To create a basic message box in PowerShell Studio, type <b>msgbox<\/b> and then press TAB. The <b>msgbox<\/b> snippet calls the static Show method with strings for the text and caption.<\/p>\n<pre lang=\"PowerShell\">[void][System.Windows.Forms.MessageBox]::Show(\"Text\",\"Caption\")<\/pre>\n<p>If you want something fancier, you can use the variety of overloads for the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/System.Windows.Forms.MessageBox.Show(v=vs.110).aspx\">Show<\/a> method.<\/p>\n<p>Here&#8217;s the condition that creates and opens a message box when the Resolve-Path command fails. After displaying the message box, I call the <b>Focus<\/b> method of the input text box, which places the cursor in the input box, showing the user how to try again.<\/p>\n<pre lang=\"PowerShell\">elseif (!(Resolve-Path $file))\r\n{\r\n    $errorMsg = \"Can't find $($Error[0].CategoryInfo.TargetName). `\r\n        Check the path and file name and try again.\"\r\n    $caption = \"Path error\"\r\n    [System.Windows.Forms.MessageBox]::Show($errorMsg, $caption)\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Use an Error Provider<\/h2>\n<p>An <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/System.Windows.Forms.ErrorProvider.aspx\">ErrorProvider<\/a> control (see <a href=\"http:\/\/info.sapien.com\/index.php\/guis\/gui-controls\/spotlight-on-the-errorprovider-control\">PowerShell Studio-specific info<\/a>) displays a little icon beside an input field. By default, the icon blinks every 250 milliseconds and when you hover over the icon, it displays a tool tip.<\/p>\n<p>You can use the error provider to indicate an error on a form.<\/p>\n<p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image013.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border: 0px;\" title=\"clip_image013\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2015\/01\/clip_image013_thumb.png\" alt=\"clip_image013\" width=\"458\" height=\"144\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>To add an error provider control to your form, in the PowerShell Studio Toolbox, double-click <b>ErrorProvider<\/b>. You do not need to position the <b>ErrorProvider<\/b> near the input box. You use code to associate it.<\/p>\n<p>To activate the error provider, use its <b>SetError<\/b> method. The first argument is the control that you want to associate with the error provider; the second argument is the text of the tool tip.<\/p>\n<p>For example, in this code, <b>errorprovider1<\/b> appears beside <b>$textboxInput<\/b> when running the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113418\">Test-Path<\/a> cmdlet on the value in the text box fails.<\/p>\n<pre lang=\"PowerShell\">elseif (!(Test-Path $file))\r\n{\r\n    $errorprovider1.SetError($textboxInput, \"File not found. Try again.\")\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Write Useful Error Messages<\/h2>\n<p>I&#8217;ve reviewed a few simple ways to display errors in a GUI application. These are essential, because errors don&#8217;t appear automatically.<\/p>\n<p>But the best thing that you can do for users is beyond the scope of this post &#8212; and that is to write clear error messages that explain the problem, how to fix it, and how to prevent it.<\/p>\n<p><em>June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at <\/em><a href=\"mailto:juneb@sapien.com\"><em>juneb@sapien.com<\/em><\/a><em> or follow her on Twitter at <\/em><a href=\"https:\/\/twitter.com\/juneb_get_help\"><em>@juneb_get_help<\/em><\/a><em>.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Follow @juneb_get_help In a previous post, I talked about displaying output in a GUI application. But I really covered only the primary output of cmdlets. In this post, we&#8217;ll discuss some very basic strategies for detecting and displaying terminating and non-terminating errors in a GUI application. This is a critical topic. If you&#8217;re used to [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[932,703],"tags":[934,1016],"class_list":["post-8621","post","type-post","status-publish","format-standard","hentry","category-beginners","category-powershell-studio","tag-juneb","tag-powershell-studio"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8621","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=8621"}],"version-history":[{"count":26,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8621\/revisions"}],"predecessor-version":[{"id":12894,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8621\/revisions\/12894"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=8621"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=8621"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=8621"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}