本記事は PowerShell Advent Calendar 2018 の 14 日目です。
100% PowerShellで書かれたPolarisというWebフレームワークの使い方を紹介します。
※Polarisは誕生してまだ1年あまりの試験的なプロジェクトのため、
ここで記載した情報はすぐに古くなる可能性があります。(念のため)
Polarisとは
Polaris1とは、MicrosoftのPowerShellチームがOSSとして開発しているPowerShell製のWebフレームワークです。
項目 | 説明 |
---|---|
開発元 | Microsoft |
登場時期 | 2017年10月3日(Initial commit) |
最新版 | 0.2.0 / 2018年12月6日3 |
特徴
サーバーの起動・停止はもとより、Web-APIリソースの追加、DocumentRootの指定などをすべてコマンドレットで行えることができます。
最近、「〇〇で爆速Webアプリ開発生活始めてみた!」のような記事が注目されています(偏見?)が
シンプルなGETのWeb-APIであれば、ワンラインで実装(もちろん、ホットデプロイ)できるので、
これ以上爆速があるのか?と思わせるくらい速い実装&実行が可能です。
ただ、サポートされる機能が非常にシンプルなため、一般的なWebアプリにはまだ使用することは現実的ではないと思っています。
Mockサーバーなど、手軽に利用したいものに関しては有効かもしれません。
環境構築
PolarisはPowerShell 5 以上が必要です。
PowerShellGetで取得できます。
Install-Module Polaris
使い方
以下、Polarisの基本形。
#Requires -Version 5.0
#Requires -Modules Polaris
$ErrorActionPreference = 'stop'
New-PolarisGetRoute -Path "/helloworld" -Force -Scriptblock {
$Response.Send('Hello World!')
}
PS> . .\SampleServer.ps1
PS> Start-Polaris
#=> Port : 8080
#=> RouteMiddleWare : {}
#=> ScriptblockRoutes : {[/helloworld, System.Collections.Generic.Dictionary`2[System.String,System.Management.Automation.ScriptBlock]]}
#=> GetLogsString : PolarisLogs
#=> ClassDefinitions :
#=> ContextHandler : System.AsyncCallback
PS> Invoke-RestMethod http://localhost:8080/helloworld
Hello World!
ターミナルをサーバーとクライアントとで2つに分けているのは、
Start-Polaris
を実行したターミナルだとなぜかInvoke-WebRequest
ができなかったためです。
※Issuesにあるかと思いましたが、何も報告されていなかった。(仕様?環境による?)
CRUDのWeb-APIサンプル
以下、蔵書管理システムのCRUDをWeb-APIで実装していきます。
先ほどドット演算子で読み込んだ SampleServer.ps1
にWeb-APIのリソースを追加します。
各APIの仕様
~\Documents\Polaris
内にFileStorage
ディレクトリを作成し、
1ファイル1書籍データでJSON形式に管理していきます。
ファイル名はユニークである必要があるため、ISBNコードを使用することにします。
以下、サンプルです。
{
"isbn" : 9781788838986,
"title" : "Learn PowerShell Core 6.0",
"publisher" : "Packt Publishing"
}
APIはもちろんCRUDなので、以下の4つを作成します。
| Path| Method| Description |
|--------------|---------|------------------|-------------------------------|
| /books | POST | 新しい書籍を登録します。 |
| /books | GET | 一致するISBNの書籍情報を取得します。 |
| /books | PUT | 一致するISBNの書籍情報を更新します。 |
| /books | DELETE | 一致するISBNの書籍情報を削除します。 |
基本的に、RequestとResponseともapplication/json
で通信させていきます。
下準備(Middlewareの実装)
先に~\Documents\Polaris\
ディレクトリを作成しておきます。
New-Item "~\Documents\Polaris\" -ItemType Director -Force
次に、各API特有のロジックに入る前に行う共通処理(Middleware)を実装します。
Middlewareですが、通常はOSとアプリケーションの間のプログラムを指す言葉ですが、
PlarisにおいてはPolarisとアプリケーションロジックの間(Middle)を示す用語として使用されています。
# $Request.BodyにRequestで受け取ったJSON文字列をオブジェクトとして格納する。
Use-PolarisJsonBodyParserMiddleware
# Requestで受け取った書籍情報を保存するディレクトリを作成、および、パスを設定する。
New-PolarisRouteMiddleware -Name FileStorage -Force -ScriptBlock {
$PolarisPath = "~\Documents\Polaris\FileStorage"
if (-not (Test-Path $PolarisPath)) {
New-Item $PolarisPath -ItemType Directory > $null
}
$Request | Add-Member -Name PolarisPath -Value $PolarisPath -MemberType NoteProperty
}
修正したSampleServer.ps1
を起動中のPolarisサーバーに適用したい場合は、
再度. .\SampleServer.ps1
コマンドを実行すれば大丈夫です。
※以降も同様。
POST
それでは、新しい書籍を登録するAPIから実装をしていきます。
# 書籍登録API
New-PolarisPostRoute -Path "/books" -Force -Scriptblock {
if (-not $Request.Body.ISBN -or -not $Request.Body.Title) {
$Response.SetStatusCode(400)
$Response.Send("ISBN or Title may not be empty.")
return
}
New-Item -ItemType File -Path $Request.PolarisPath -Name "$($Request.Body.ISBN).json" -Value $Request.BodyString > $null
}
では、作成したAPIの動作確認をしていきます。
# 修正を適用
PS> . .\SampleServer.ps1
# 1件目
PS> $book = @{isbn = 9781788838986; title = 'Learn PowerShell Core 6.0'; publisher = 'Packt Publishing'}
PS> Invoke-RestMethod http://localhost:8080/books -Method Post -Body ($book | ConvertTo-Json -Compress)
# 2件目
PS> $book = @{isbn = 9781633430297; title = 'Windows PowerShell in Action'; publisher = 'Manning Publications'}
PS> Invoke-RestMethod http://localhost:8080/books -Method Post -Body ($book | ConvertTo-Json -Compress)
# FileStorageに登録した書籍ごとにjsonファイルが作成されている。
PS> ls ~\Documents\Polaris\FileStorage
#=>
#=> ディレクトリ: C:\Users\uokeaj\Documents\FileStorage
#=>
#=> Mode LastWriteTime Length Name
#=> ---- ------------- ------ ----
#=> -a---- 2018/12/13 21:10 96 9781633430297.json
#=> -a---- 2018/12/13 21:10 89 9781788838986.json
GET
次に、一致するISBNの書籍情報を取得できるようにします。
リクエストパラメーターは$Request.Query['パラメーター名']
で取得可能です。
# 書籍取得API
New-PolarisGetRoute -Path "/books" -Force -Scriptblock {
$query = if ($Request.Query['isbn']) {$Request.Query['isbn']} else {'*'}
$gciParams = @{
Path = $Request.PolarisPath
Filter = "$($query).json"
ErrorAction = 'SilentlyContinue'
}
$books = Get-ChildItem @gciParams | Foreach-Object {$_ | Get-Content | ConvertFrom-Json}
$Response.Send(($books | ConvertTo-Json));
}
# 修正を適用
PS> . .\SampleServer.ps1
# 1件だけ取得
PS> Invoke-RestMethod http://localhost:8080/books?isbn=9781633430297
#=> publisher title isbn
#=> --------- ----- ----
#=> Manning Publications Windows PowerShell in Action 9781633430297
# 全件取得
PS> Invoke-RestMethod http://localhost:8080/books
#=> publisher title isbn
#=> --------- ----- ----
#=> Manning Publications Windows PowerShell in Action 9781633430297
#=> Packt Publishing Learn PowerShell Core 6.0 9781788838986
PUT
今度は、一致するISBNの書籍情報を更新します。
POSTの時と実装の仕方はあまり変わりません。
# 書籍更新API
New-PolarisPutRoute -Path "/books" -Force -Scriptblock {
if (-not $Request.Body.ISBN -or -not $Request.Body.Title) {
$Response.SetStatusCode(400)
$Response.Send("ISBN or Title may not be empty.")
return
}
Set-Content -Path (Join-Path $Request.PolarisPath "$($Request.Body.ISBN).json") -Value $Request.BodyString
}
# 修正を適用
PS> . .\SampleServer.ps1
# 試しに、'Learn PowerShell Core 6.0'の出版社情報を省略した状態で更新してみます。
PS> $book = @{isbn = 9781788838986; title = 'Learn PowerShell Core 6.0'}
PS> Invoke-RestMethod http://localhost:8080/books -Method Put -Body ($book | ConvertTo-Json -Compress)
# publisherプロパティが消えていることが確認できました。
PS> Invoke-RestMethod http://localhost:8080/books?isbn=9781788838986
#=> isbn title
#=> ---- -----
#=> 9781788838986 Learn PowerShell Core 6.0
DELETE
最後に、一致するISBNの書籍情報を削除できるようにします。
こちらも、POST・PUTとあまり変わりません。
# 書籍削除API
New-PolarisDeleteRoute -Path "/books" -Force -Scriptblock {
if (-not $Request.Body.ISBN) {
$response.SetStatusCode(400)
$response.Send("ISBN may not be empty.")
return
}
Remove-Item -Path (Join-Path $Request.PolarisPath "$($Request.Body.ISBN).json")
}
# 修正を適用
PS> . .\SampleServer.ps1
PS> Invoke-RestMethod http://localhost:8080/books -Method Delete -Body '{"isbn":9781788838986}'
# 削除できていることが確認できました。
PS> Invoke-RestMethod http://localhost:8080/books?isbn=9781788838986
#=> No output
Webページの表示方法
Polarisでは、まだテンプレートエンジンで動的なWebページ画面を生成する機能はないですが、
静的ページであれば、New-PolarisStaticRoute
というコマンドレットで簡単に表示させることができます。
PS> New-Item "~\Documents\Polaris\Public" -ItemType Director -Force
PS> @"
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Hello world!</title>
</head>
<body>
PolarisでHTMLページを表示しました。
</body>
</html>
"@ | Out-File "~\Documents\Polaris\Public\index.html" -Encoding utf8
PS> New-PolarisStaticRoute -RoutePath 'public' -FolderPath "~\Documents\Polaris\Public" -Force
PS> Start-Process http://localhost:8080/public/index.html
現在、PSHTMLというDSLでもっとHTMLソースを扱いやすくできるようにするための提案がされています。
RFC #002: Bundle PSHTML with Polaris #165
今後のアップデートに注目ですね。
参考文献
Polaris docs
Preview Online Code Files Learn PowerShell Core 6.0
-
Polaris(ポラリス、pəlˈærɪs : 北極星)という命名ですが、おそらく、PowerShell + Solaris から来ていると思われます。(推測)
100% PowerShell2で実装されており、Windows PowerShellとPowerShell Coreの両方で動作可能となっています。 ↩ -
発足時はコア部分だけC#で実装されていましたが、2018年4月頃に「WIP: Pure PowerShell Implementation」でPowerShellのみを使用するようにリファクタリングされました。
現在、PoC(Proof of Concept)として開発されており、Microsoft製品としてはサポート対象外とされています。 ↩ -
配布先:PowerShell Gallery
| リポジトリ | https://github.com/PowerShell/Polaris |
| プログラミング言語 | PowerShell |
| 対応OS | Cross-platform (Windows, Linux or Mac) |
| ライセンス | MIT License |
| 公式ドキュメント | https://powershell.github.io/Polaris/ | ↩