Click here for the Medium post.
There are many articles which focus on either using Javascript or Typescript in .NET Core for the purpose of the client interface, whether it be for Angular, React or general single page applications. This is focussed purely on executing Javascript and Typescript from an ASP .Net Core application. This is achieved using the Microsoft.AspNetCore.NodeServices Nuget package as part of the JavascriptServices toolset.
At some point in the past you were probably deciding what server-side technology should I use for the server, namely a web or rest API. If you are from a .net background you would probably choose ASP .net core over Node JS. There are many other articles comparing the two. So you chose .Net Core for good reasons and you were looking for some thirdparty packages but could not find any suitable ones on Nuget. There is a huge selection of javascript libraries available on the node package manager (NPM). For example in my instance I needed to generate PDF documents and although there were a couple options on Nuget those available for both server and client Javascript looked much more favourable. I want to seemlessly from my .net code make calls to such libraries. As well you might have written your own Typescript or Javascript libraries and would simply like to reuse the code alongside your c# / .net core project. Secondly, as the rest of my code is in c# to keep my server side code robust a strongly typed option would be better. Therefore I would like to add Typescript files at will to my visual studio project and seemless call this code. Hence, this post will focus on setting up a .net core project to do just this with examples of calling both Javascript directly and Typescript code.
Prerequiste | Download |
---|---|
Node JS | https://nodejs.org/en/ |
NET Core 2.1 | https://www.microsoft.com/net/download |
Dependency | Further Information |
---|---|
Microsoft.AspNetCore.NodeServices | https://www.nuget.org/packages/Microsoft.AspNetCore.NodeServices/ |
Dependency | Further Information |
---|---|
JavaScriptServices | https://github.com/aspnet/JavaScriptServices |
PDF Make (example JS dependency used) | http://pdfmake.org |
Simple add the following line to ConfigureServices in your Startup.cs file:
Public void ConfigureServices(IServiceCollection services)
{
//
services.AddNodeServices();
//
}
Next We create a wrapper class called JavaScriptService.cs. This contains the following constructor. The NodeServices instance automatically gets injected.
public JavaScriptService([FromServices]INodeServices nodeServices, string scriptFolder)
{
_nodeServices = nodeServices;
_scriptFolder = scriptFolder;
}
This is achieved by registering our new IJavaScriptService instance in the ConfigureServices method in Startup.cs which will now look like this:
Public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IJavaScriptService, JavaScriptService>();
// Add node js
services.AddNodeServices(options =>
{
//options.LaunchWithDebugging = true;
//options.UseSocketHosting();
});
}
So to use our node js wrapper from our ValuesController.cs class we simply include the service as a dependency via the constructor:
private IJavaScriptService _javaScriptService;
public ValuesController(IJavaScriptService javaScriptService)
{
_javaScriptService = javaScriptService;
}
And to use the service for example the 'Hello' typescript function in ValuesController.cs we do the following:
// GET api/values/hello
[HttpGet("hello")]
public async Task<IActionResult> Hello()
{
string ret = await _javaScriptService.Hello("Michael");
return Ok(ret);
}
Which calls the 'Hello' method inside JavaScriptService.cs
public async Task<string> Hello(string name)
{
string path = Path.Combine(_scriptFolder, "./scripts/hello");
var result = await _nodeServices.InvokeAsync<string>(path, name);
return result;
}
And that is it!
The hello.ts typescript file automatically gets compiled into a corresponding javascript file called hello.js which also lives in the scripts folder.
On execution the node services picks the JS up from the ./scripts/hello path and executes accordingly.
Visual Studio should automatically install the node dependencies as per the Package.json file, but just to be sure you can run the follow from the project folder.
npm install
or
yarn
To install the nuget dependencies this should also be automatic, however, you can also do this from command line:
dotnet restore
To run the project you can click F5 in Visual Studio for debug mode or from command line in the project folder:
dotnet run
Finally once up and running test the addNumbers function from the browser:
http://localhost:5000/api/values/addNumbers
This should return '7'
To test the TypeScript Make PDF function try the following:
http://localhost:5000/api/values/makepdf
This should return the PDF straight into the browser (depending on your browser and configuration), or will return a PDF file to download.
Add the following to the unit test post build as this may be the best way to put the javascript files from the main project in to the target test folder:
copy $(SolutionDir)MikeyFriedChicken.DotNetCoreTypeScript\scripts\*.* $(TargetDir)scripts
When calling the JavaScriptService wrapper the following base paths were needed so that the scripts could be correctly resolved:
private string SCRIPT_FOLDER_TS = "."; // Works for TS javascript files
private string SCRIPT_FOLDER_JS = "./bin/Debug/netcoreapp2.1"; // Works for pure javascript files
To initialise the the JavaScriptService from a unit test the following code is used: you will note the project path / where the node_modules are located had to be specified
var services = new ServiceCollection();
services.AddNodeServices(options => {
// Set any properties that you want on 'options' here
options.ProjectPath = "../../../";
});
var serviceProvider = services.BuildServiceProvider();
var nodeServices = serviceProvider.GetRequiredService<INodeServices>();
Sometimes when building you might get the following error:
Error TS2403 (TS) Subsequent variable declarations must have the same type. Variable 'module' must be of type 'any', but here has type 'NodeModule'.
To resolve this change, double click on the error and it will take you to the index.d.ts file with the error. Simply change the following and save:
declare var module: NodeModule;
to
declare var module: any;