Powershell — удобная API построенная на .net. Powershell позволяет пользователям писать скрипты, не упираясь в программирование, при этом получая схожие результаты. Что происходит на КДВП, автор объяснит позже по тексту. Сейчас нам срочно нужно притвориться, что мы программируем на C#.
TL;DR: Postman не нужен, если есть Powershell. Но сперва нужно зайти издалека.
Делаем простой класс
Автор слышал, что крутые программисты делают все через классы и их методы.
Так как PowerShell это позволяет, давайте автор покажет, как можно сложить 1 + 1 притворившись, что мы программируем.
class ClassName {
[string] Sum ($A, $B) {
$Result = $A + $B
return $Result
}
}
Вот наш класс ClassName и его метод Sum. Экземпляр класса можно вызвать ровно так же, как в настоящих языках программирования.
$NewClass = [ClassName]::new()
$NewClass.Sum(1, 1)
Создаем новый экземпляр класса и вызываем метод, всё просто.
Есть ли Void в Powershell
При написании сложных скриптов этот же вопрос вставал у автора. Как сделать функцию, которая будет Void?
Говорят, что можно сделать так:
Get-Date | Out-Null
Однако, | Out-Null так же глушит весь Verbose, ErrorAction и не работает с Invoke-Command.
Если вам нужна функция с [Void] – делайте новый класс, другого выхода нет.
class ClassName {
#Конструктов класса
[void] Start () {
#Создаем экземпляр класса прямо внутри этого же класса.
$q = [ClassName]::new()
$q.GetDate()
}
#Своего рода метод внутри класса
[void] GetDate () {
#А вот тут вызываем еще один метод из .Net
#Просто так, потому что можем
$Result = [DateTime]::UtcNow.ToString()
Write-Host $Result
}
}
Сделать метод приватным внутри одного класса, или вызвать один из методов класса внутри этого же класса в PowerShell невозможно, поэтому приходится лепить такие вот вызовы.
Конструктор класса был добавлен в пример для понимания ограничений языка и такой код писать в целом не стоит.
Вот так мы добились того, что не заглушили Verbose, при этом сделали функцию с Void.
Список методов класса
Скажем, вам нужно сделать программу, возможно, даже на языке, который вы не знаете. Вы знаете, что есть какой-то класс, но методы его плохо документированы.
Перечислить все методы интересующего класса можно так:
#Делаем любовь, а не класс
$Love = [ClassName]::new()
#Выбираем члены интересующего нас класса и записываем в массив.
foreach ($i in $Love | Get-Member -MemberType Method | Select-Object name) {
[array]$array += $i.Name
}
#Вызываем члены класса, если нужно.
$Array | ForEach-Object {
$Love.$_()
}
Отправляем HTTP запросы скриптом (оправдываем КДПВ)
Классами мы можем представлять данные и эти данные конвертировать в разные форматы. К примеру, нам нужно отправить POST запрос на веб сайт в формате JSON.
Сначала мы делаем модель данных и заполним данные в новый экземпляр.
#В качестве модели данных делаем новый класс
class DataModel {
$Data
$TimeStamp
}
#Создаем экземляр класса
$i = [DataModel]::new()
#Заполняем данные
$i.Data = "My Message in string"
$i.TimeStamp = Get-Date
Так выглядит экземпляр класса после заполнения:
PS C:\> $i
Data TimeStamp
---- ---------
My Message in string 30.07.2020 5:51:56
Потом этот экземпляр можно конвертировать в XML или JSON или даже SQL запрос. Остановимся на JSON:
#Конвертируем данные в JSON
$Request = $i | ConvertTo-Json
Так выглядит JSON после его конвертации:
PS C:\> $Request
{
"Data": "My Message in string",
"TimeStamp": "2020-07-30T05:51:56.6588729+03:00"
}
И отправляем:
#Отправляем JSON
Invoke-WebRequest localhost -Body $Request -Method Post -UseBasicParsing
В случае если нужно отправлять один и тот же JSON файл 24/7, можно сохранить его как файл и отправлять уже из файла. К примеру, возьмем этот же самый $Request.
#Сохраняем данные конвертированные ранее в JSON в файл
$Request | Set-Content C:\Users\User\Desktop\YourRequest.json
#Отправляем ранее сохраненный в файл JSON
Invoke-WebRequest localhost -Body (Get-Content C:\Users\User\Desktop\YourRequest.json) -Method Post -UseBasicParsing
Получаем HTTP запросы скриптом (оправдываем КДПВ 2)
Автор терпеть не может Postman, зачем кому-либо нужен Postman, когда есть руки и PowerShell? (Автор предвзято относится к этой программе и его нелюбовь ничем не обоснована.)
Делать свою альтернативу мы будем это с помощью System.Net.HttpListener, то есть мы сейчас запустим настоящий веб сервер из скрипта.
#Создаем новый экземпляр класса
$http = [System.Net.HttpListener]::new()
#Добавляем HTTP префиксы. Их может быть сколько угодно
$http.Prefixes.Add("http:/localhost/")
$http.Prefixes.Add("http://127.0.0.1/")
#Запускаем хттп листенер
$http.Start()
$http.Close()
Так проходит запуск класса.
Экземпляр класса был создан и его процесс запустился, мы можем слушать от него вывод. Вывод представлен как System.Net.HttpListener.GetContext. В это примере мы принимаем и конвертируем только POST запрос.
while ($http.IsListening) {
#GetContext нужен для получения сырых данных из HttpListener
$context = $http.GetContext()
#Определяем тип запроса с помощью Request.HttpMethod
if ($context.Request.HttpMethod -eq 'POST') {
#Читаем сырые данные из GetContext
#Для каждого отдельного запроса создаем свой конвейер
[System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() | ForEach-Object {
#С помощью System.Web.HttpUtility делаем urlDecore, иначе кириллица превращается в руны
$DecodedContent = [System.Web.HttpUtility]::UrlDecode($_)
#Конвертируем прилетевшие данные в нужный нам формат
$ConvertedForm = $DecodedContent | ConvertFrom-Json -ErrorAction SilentlyContinue
#Cконвертированные данные отображаем таблицой
$ConvertedForm | Format-Table
}
}
}
Готовый скрипт
С помощью этого скрипта можно принимать запросы:
#Создаем новый экземпляр класса
$http = [System.Net.HttpListener]::new()
#Добавляем HTTP префиксы. Их может быть сколько угодно
$http.Prefixes.Add("http://localhost/")
$http.Prefixes.Add("http://127.0.0.1/")
#Запускаем веб листенер
$http.Start()
if ($http.IsListening) {
Write-Host "Скрипт запущен"
}
while ($http.IsListening) {
#GetContext нужен для получения сырых данных из HttpListener
$context = $http.GetContext()
#Определяем тип запроса с помощью Request.HttpMethod
if ($context.Request.HttpMethod -eq 'POST') {
#Читаем сырые данные из GetContext
#Для каждого отдельного запроса создаем свой конвейер
[System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() | ForEach-Object {
#С помощью System.Web.HttpUtility делаем urlDecore, иначе кириллица превращается в руны
$DecodedContent = [System.Web.HttpUtility]::UrlDecode($_)
#Конвертируем прилетевшие данные в нужный нам формат
$ConvertedForm = $DecodedContent | ConvertFrom-Json -ErrorAction SilentlyContinue
#Cконвертированные данные отображаем таблицей
$ConvertedForm | Format-Table
}
#Отвечаем клиенту 200 OK и закрываем стрим.
$context.Response.Headers.Add("Content-Type", "text/plain")
$context.Response.StatusCode = 200
$ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
$context.Response.ContentLength64 = $ResponseBuffer.Length
$context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
$context.Response.Close()
}
#Cконвертированные данные отображаем таблицей
$http.Close()
break
}
Данные будут автоматичеки конвертироваться из JSON и выводиться в терминал.
Автор надеется, что вы выбросите Postman, так же, как и GIT с GUI.