KillAngularApp.ps1
param (
[Parameter(Mandatory=$true)][string] $portToFind
)
# This PS script requires the paramter $portToFind to be passed into it
pwd
Write-Host Probing for Angular App running at $portToFind
$runningEudAppProcessLocal = 'netstat -ano | findstr "$portToFind"'
$arr = Invoke-Expression $runningEudAppProcessLocal
# $runningEudAppProcessLocal
$arr = $arr -split '\s+'
Write-Host Probing complete:
$arr
if ($arr.Length -ge 5) {
$runningAngularAppPort = $arr[5]
$runningAngularAppPort
Write-Host Killing the process..
$killScript = "taskkill /PID $runningAngularAppPort /F"
Invoke-Expression $killScript
Write-Host probing once more
$arr = Invoke-Expression $runningEudAppProcessLocal
if ($arr.Length -eq 0){
Write-Host There is no running process any more at $portToFind
}
}
The powershell script above runs 'netstat ano -findstr "someportnumber"'. It finds the PID at that port and splits the result using the '\s+' expression, i.e. whitespace.
If we find a PID (process id), we stop that process using the 'taskkill' command with the '-force' flag.
We then need to have a C# class that .Net Core can call. The code here has been tested with .Net Core 3.0 and 3.1 successfully.
KillAngularPortHelper.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
namespace EndUserDevice
{
public static class AngularKillPortHelper
{
/// <summary>
/// Kills Angular app running with for example node at configured port Host:SpaPort
/// </summary>
public static void KillPort()
{
try
{
string environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
string configFile = environmentName == Environments.Development ? "appsettings.json" :
environmentName == Environments.Staging ? "appsettings.Staging.json" :
environmentName == Environments.Production ? "appsettings.Production.json" : "appsettings.json";
// Set up configuration sources.
var config = new ConfigurationBuilder()
.AddJsonFile(configFile, optional: false)
.Build();
string angularAppPort = config.GetValue<string>("Configuration:Host:SpaPort");
if (environmentName == Environments.Development)
{
string killAngularAppRunningAtPortMessage =
$"Trying to shutdown existing running Angular SPA if it is running at reserved fixed port: {angularAppPort}";
Debug.WriteLine(killAngularAppRunningAtPortMessage);
Console.WriteLine(killAngularAppRunningAtPortMessage);
//requires Nuget Packages:
//Microsoft.Powershell.SDK
//System.Management.Automation
string ps1File = config.GetValue<string>("Configuration:Host:CloseSpaPortScriptPath");
var startInfo = new ProcessStartInfo();
startInfo.FileName = "powershell.exe";
startInfo.Arguments = "-noprofile \"& \"\"" + ps1File + "\"\"\"";
startInfo.Arguments += " -portToFind " + angularAppPort;
startInfo.UseShellExecute = false;
//WARNING!!! If the powershell script outputs lots of data, this code could hang
//You will need to output using a stream reader and purge the contents from time to time
startInfo.RedirectStandardOutput = !startInfo.UseShellExecute;
startInfo.RedirectStandardError = !startInfo.UseShellExecute;
//startInfo.CreateNoWindow = true;
var process = new System.Diagnostics.Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit(3*1000);
//if you want to limit how long you wait for the program to complete
//input is in milliseconds
//var seconds_to_wait_for_exit = 120;
//process.WaitForExit(seconds_to_wait_for_exit * 1000);
string output = "";
if (startInfo.RedirectStandardOutput)
{
output += "Standard Output";
output += Environment.NewLine;
output += process.StandardOutput.ReadToEnd();
}
if (startInfo.RedirectStandardError)
{
var error = process.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(error))
{
if (!string.IsNullOrWhiteSpace(output))
{
output += Environment.NewLine;
output += Environment.NewLine;
}
output += "Error Output";
output += Environment.NewLine;
output += error;
}
}
Console.WriteLine(output);
Debug.WriteLine(output);
}
}
catch (Exception err)
{
Console.WriteLine(err);
}
}
}
}
Finally we can configure our app to use the ports we want to use like this:
appsettings.json
"Configuration": {
"Host": {
"SpaPort": "44394",
"ApiPort": "44364",
"CloseSpaPortScriptPath": "c:\\dev\\someapp\\somesubdir\\KillAngularApp.ps1"
},
}
The ApiPort here is not used by our code in our article. The SpaPort however is used.
We then call this helper class like this in Program.cs of our asp.net core application hosting the Angular Spa:
Program.cs
public class Program
{
public static void Main(string[] args)
{
AngularKillPortHelper.KillPort();
IWebHost host = CreateWebHostBuilder(args).Build();
host.Run();
}
//..
This makes sure that the port is ready and not buy by another app (such as your previous debugging session of the app!) and by killing the app running with node at that port, we make sure that Angular can run at a fixed port. Are we done yet? No! We must also update the package.json file to use that port we want to use! This must correspond to the port we configure in the appsettings.json file to kill in the first place. (i.e. make sure the port is freely available and not busy)
package.json
{
"name": "enduserdevice",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve -o --ssl true --ssl-key /node_modules/browser-sync/certs/server.key --ssl-cert /node_modules/browser-sync/certs/server.crt --port=44394 &REM",
"build": "ng build --base-href /eud/ --source-map --prod",
"build-staging": "ng build --base-href /eud/ --source-map --staging",
"build-mobile": "ng build --base-href /eud2/ --source-map -c mobile",
"prod-build-dev": "ng build --prod --source-map --base-href /eud/ --prod",
"build:ssr": "ng run EndUserDevice:server:dev",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
Never mind much of the setup above, the important bit to make note of here is the part:
--port=44394 &REM
The adjustments of the ng start script makes sure we use a fixed port of our Angular app. Sadly, as you can see - this is not easily configurable as we hard code it into our package.json.
Maybe we could set this up as an environment variable in a Angular environment file.. ?
Hope you found this article interesting. Having a fixed port is always handy when developing locally Angular app and you do no not like this random port thingy that is default set up otherwise.
Works on my PC!