Powershell удобная API построенная на .net. Powershell позволяет пользователям писать скрипты, не упираясь в программирование, при этом получая схожие результаты. Что происходит на КДВП, автор объяснит позже по тексту. Сейчас нам срочно нужно притвориться, что мы программируем на C#.
TL;DR: Postman не нужен, если есть Powershell. Но сперва нужно зайти издалека.
Делаем простой класс
Автор слышал, что крутые программисты делают все через классы и их методы.
Так как PowerShell это позволяет, давайте автор покажет, как можно сложить 1 + 1 притворившись, что мы программируем.
class ClassName { [string] Sum ($A, $B) { $Result = $A + $B return $Result }}
Вот наш класс ClassName и его метод Start. Экземпляр класса можно вызвать ровно так же, как в настоящих языках программирования.
$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"}
И отправляем:
#Отправляем JSONInvoke-WebRequest localhost -Body $Request -Method Post -UseBasicParsing
В случае если нужно отправлять один и тот же JSON файл 24/7, можно сохранить его как файл и отправлять уже из файла. К примеру, возьмем этот же самый $Request.
#Сохраняем данные конвертированные ранее в JSON в файл$Request | Set-Content C:\Users\User\Desktop\YourRequest.json #Отправляем ранее сохраненный в файл JSONInvoke-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.