This post describes how you can deliver JSON/XML/HTML APIs quickly with Sitecore PowerShell Extensions.
Technically this was also available earlier but the API was not refined to the state it is now.
As Sitecore is constantly progressing from predominantly serving as a CMS towards becoming a mobile and web application delivery platform (which is very apparent by the recent increase of SPEAK popularity, the introduction of Item Web API and the app centric nature of the new Sitecore 8 interface there is an increased need to rapidly deliver APIs for those those front end applications to work seamlessly with the CMS back-end.
PowerShell Extensions can help you with that move by enabling rapid prototyping of APIs that are either JSON or XML in nature.
How to make scripts available for the Web API?
This functionality is available in Sitecore PowerShell Extensions starting from version 2.5, however I was never happy with how it worked and how the URLs were structured. With the Modules functionality introduced in 2.8 it was a good time to model it properly for SPE 3.0.
To make a script callable through the v2 of the API you need to place it in an Enabled module in the Web API
integration point library.
SPE will automatically make all the scripts available under this folder executable from its Web API service. If you don’t have the library in your module you can simply create a new Library named Web API
or run the Create libraries for integration points wizard by right clicking the module in the Content Editor.
And executing the wizard as follows:
How to call the exposed scripts?
The syntax of the URL looks as follows:
Authorization, authentication and anonymous users
If you’re already working as a logged in user there is n need to provide credentials and you can work with all databases that are available to your user group, however if you’re not authorized you can place 2 additional parameters: user
and password
to provide the missing credentials. So the above URL can look as follows: http://hostname/-/script/v2/master/ChildrenAsJson?item=content&user=admin&password=b
which will result in a request authenticated as the standard Sitecore admin.
If you need to call the Web API script as an unauthenticated user – that’s also fine however the database
switch will not work (which is an intentional behaviour) and only the scripts that are published to your target database will work. The API will prevent the switch and automatically default to the public database. In this case you need to make sure that the module in the enabled state as well as your scripts are published to the target database.
Caution! If you’re using a version of the SPE module previous to 3.1 and want to run scripts from a database different than the context database e.g. master
you need to provide additional parameter scriptDb=master
to the parameters list. This is due to a bug in the URL rewriting I’m doing and is fixed in version 3.1 of the module. Credits for finding this and reporting goes to a colleague of mine from Cognifide – Andrzej Pucilowski!
How script names are resolved?
The script name provided in the URL will be resolved among all the enabled modules’ Web API
libraries. The script name is relative to the Web API
library. This allows you to switch between versions of your API versions easily by having 2 modules with the same script names and enable one module while disabling the other to switch between versions.
What are the variables in the URL and how do I use them?
All variables that are provided in the URL will be passed to your script as PowerShell variables, so the item
parameter specified above will be available to your script as a PowerShell $item
variable. To avoid name collisions or malicious attempts at modifying the script behaviour by overriding system variables any parameter that is already defined as a PS variable prior to script execution in the environment will be skipped. so you cannot override e.g. $HOME
or $true
.
Error detection
Should your script encounter any execution errors the API will respond with the HTTP Code 424 Method Failure
and should produce the error information as well as the script output.
Writing Scripts to respond to API calls
The API call will respond with whatever your script produces as an output in text form. By default the response will be of MIME type text/plain
but you can modify it easily as follows:
if ($HttpContext -ne $null){ $HttpContext.Response.ContentType = "application/json"; }
You also need to make sure your response is in the proper format depending on the response, but PowerShell is of great help here providing us with the support of conversion in form of ConvertTo-Json
, ConvertTo-Xml
, ConvertTo-Html
or even ConvertTo-Csv
cmdlets.
What can I do with it?
Armed in the knowledge we can start writing APIs let’s start with a JSON API that will return a basic list of children of a given item:
#if the location is not Sitecore tree, switch to root of Master Database if((Get-Location).Provider.Name -ne "CmsItemProvider"){ Set-Location master:\ } #change the path to the item that was given as an item parameter cd $item #Set the response MIME type to JSON if ($HttpContext -ne $null){ $HttpContext.Response.ContentType = "application/json"; } #if JSON serialization depth is not provided as a parameter set it to 1 if ($depth -eq $null){ $depth = 1; } #return some of the children's properties as JSON gci . | select-object @{Label="Path"; Expression={ $_.Paths.Path}} , Name, DisplayName, TemplateName, Language, Version, Paths | ConvertTo-Html -Depth $depth
Now when we save the script with a ChildrenAsJson
name to a Web API
library in our module, we can call it as follows:
http://hostname/-/script/v2/master/ChildrenAsJson?item=content&user=admin&password=b
Which will produce the following result:
You might have noticed that I’ve neglected to provide the depth
parameter which resulted in using 1
as the default, but if I specify it as 2
the URL and the output will change to:
Nice, isn’t it?
Similarly if I need my API to be in the XML format a matching script could look as follows:
if((Get-Location).Provider.Name -ne "CmsItemProvider"){ Set-Location master:\ } cd $item if ($HttpContext -ne $null){ $HttpContext.Response.ContentType = "text/xml"; } #return some of the children's properties as XML (gci . | select-object Name, DiaplayName, TemplateName, Language, Version, Paths | ConvertTo-Xml).OuterXml
Which would produce the following output:
Naturally your XML might need further work but this simple example gives you the idea on how you can approach it. As for a human readable version of the report you could do it as follows:
#Let's style it nicely: $head = '<style>'+ 'body{font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; text-align:left;}'+ 'h1 {width:100%;font-family: "Skolar Bold", Palatino, Cambria, "Droid Serif", Georgia, "Times New Roman", Times, serif; font-size: 28px;}'+ 'p {width:100%;font-family: "Proxima Nova Regular", "Helvetica Neue", Calibri, "Droid Sans", Helvetica, Arial, sans-serif; font-size: 18px}'+ 'table {font-size:12px; border-collapse:collapse; border:1px solid #69c; margin:20px;}'+ 'th{font-weight:normal; font-size:14px; color:#039; border-bottom:1px dashed #69c; padding:12px 17px;}'+ 'td{color:#668;padding:7px 17px;border-bottom: 1px dashed #d8e8ff;}'+ 'tbody tr:hover td{color:#339;background:#f0f0ff;}'+ '</style>' #and provide a title so users know what they are looking at $body = "<h1>Children of $($item)</h1>" if((Get-Location).Provider.Name -ne "CmsItemProvider"){ Set-Location master:\ } cd $item #let the browser know they are looking at an HTML output if ($HttpContext -ne $null){ $HttpContext.Response.ContentType = "text/html"; } # now query and format the output gci . | select-object Name, DisplayName, TemplateName, Language, Version, ProviderPath | ConvertTo-Html -head $head -body $body | % { [System.Web.HttpUtility]::HtmlDecode($_) }
So you can easily create scripted reports available under a simple URL to the unauthenticated users if needed.
What else?
The scripts I’ve shown focus on providing output to your external applications, but since those are just regular PowerShell scripts you can easily include actions that will modify your content as part of the execution. Naturally this is a double edged sword and you should test those properly as they are a sharp tool to expose externally. personally I use SPE to create rapid prototypes and iterate over my API usability improvements and further down the development process potentially replace those with C# implementations, not to introduce a hard dependency on SPE, but e.g. in case of reports SPE implementation is good enough and gives me flexibility that is hard to give up even in production scenarios.
Should you at some point decide that this API is not desirable on your production servers you can easily disable it as described in this issue on our GitHub repository.
This entry (Permalink) was posted on Monday, April 13th, 2015 at 1:04 pm and is filed under PowerShell, Sitecore, Software, Software Development, Solution, Web applications. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response , or trackback from your own site.