C#執行代碼塊js腳本


一、背景

在低代碼平台中為了擴展功能,我們在業務編排中會擴展代碼塊的功能,允許用戶直接在界面中進行代碼(Node.js、 Python)的編寫,來實現取數或者賦值的一些功能,類似某某雲效果:

二、代碼實現

.Net執行js的框架有很多,大致分為兩類1、瀏覽器內核/無頭瀏覽器,2、Js引擎/框架。主要區別是前者模擬瀏覽器去請求一個完整的網頁,后者則是單單去計算調用一個純粹的js方法,我更傾向於后者。
以下介紹幾種常見的Js引擎框架:
● Microsoft.AspNetCore.NodeServices (接口方法已標注過時,后續有可能不會在.net 新版本中支持)
● Microsoft.ClearScript (主要是使用v8引擎)
● JavaScriptEngineSwitcher.ChakraCore (使用的ChakraCore引擎)

1、NodeServices

1.1 前提

● Nodejs環境
● 腳本需要按照Nodejs的模塊導出的格式將方法寫成導出的形式

1.2 安裝

在項目中引入 NuGet 包:Microsoft.AspNetCore.NodeServices ,這里我使用的是 3.1.23版

1.3 實現

服務注入
修改 Startup 類,在 ConfigureServices 方法中添加下面代碼:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddNodeServices();
}

創建js文件
項目的根目錄創建jsfiles文件夾,文件中中創建一個名為 hello.js 的腳本文件,文件的屬性中的「復制到輸出目錄」設置為「始終復制」,文件的內容如下

module.exports = function (callback, name) {
  var msg = "Hello " + name;
  callback(null, msg);
};

控制器實現
控制器的構造中依賴注入INodeServices服務,使用InvokeAsync執行js文件

[ApiController]
[Route("[controller]")]
public class NodeServicesController : ControllerBase
{
    private readonly INodeServices _nodeServices;
    public NodeServicesController(INodeServices nodeServices)
    {
        _nodeServices = nodeServices;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var result = await _nodeServices.InvokeAsync<string>("./jsfiles/hello.js", "nodeServices");
        return Ok(result);
    }
}

1.4 運行效果

2、ClearScript

2.1 優點

● 支持原生js編寫
● 支持直接執行js字符串

2.2 安裝

在項目中引入 NuGet 包:Microsoft.ClearScript,這里我使用的是 7.2.3版

2.3 實現

調用腳本支持加載腳本文件和加載腳本字符串
加載腳本文件
調用engine.ComplieDocument方法直接加載js文件,然后調用engine.Execute將引入的腳本執行一遍,這樣后面就可以調用js方法,say就是js的方法名,調用格式與js相同。

V8Script script = engine.CompileDocument("./jsfiles/hellojs.js");   // 載入並編譯js文件, 然后Execute, 就可以直接調用。 
engine.Execute(script); 
var result = engine.Script.say("clearScript");  

加載腳本字符串
直接調用engine.Execute將引入的腳本執行一遍

string jsText = @"function say(name){
                    return 'Hello '+ name;    
                }";
engine.Execute(jsText); 
var result = engine.Script.say("clearScript");  

最終代碼

[ApiController]
[Route("[controller]")]
public class ClearScriptController : ControllerBase
{
    public ClearScriptController()
    {
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        string jsText = @"function say(name){
                            return 'Hello '+name;    
                        }";
        using (var engine = new V8ScriptEngine())
        {
            engine.DocumentSettings.AccessFlags = Microsoft.ClearScript.DocumentAccessFlags.EnableFileLoading;
            engine.DefaultAccess = Microsoft.ClearScript.ScriptAccess.Full;
            //V8Script script = engine.CompileDocument("./jsfiles/hellojs.js");   // 載入並編譯js文件。
            engine.Execute(jsText);  //直接執行js字符串
            var result = engine.Script.say("clearScript");  //執行方法
            return Ok(result);
        }
    }
}

2.4 運行效果

3、ChakraCore

3.1 優點

● 支持原生js編寫
● 支持直接執行js字符串

3.2 安裝

在項目中引入 NuGet 包:JavaScriptEngineSwitcher.ChakraCore

另外還需要安裝的類庫:(否則會執行報錯)
JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 ///針對Linux-x64環境下組件;
JavaScriptEngineSwitcher.ChakraCore.Native.win-x64 //針對Wind-x64環境下組件;
JavaScriptEngineSwitcher.ChakraCore.Native.win-x32 //針對Wind-x32環境下組件;

3.3 實現

與ClearScript邏輯一樣先把js文件執行一遍,然后再去調用要使用的方法。

engine.Execute(jsText);
string result = engine.CallFunction<string>("say", "chakraCoreJs"); 

最終代碼

[ApiController]
[Route("[controller]")]
public class ChakraCoreController : ControllerBase
{
    public ChakraCoreController()
    {
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        string jsText = @"function say(name){
                            return 'Hello '+name;    
                        }";
        IJsEngineSwitcher engineSwitcher = JsEngineSwitcher.Current;
        engineSwitcher.EngineFactories.Add(new ChakraCoreJsEngineFactory());
        engineSwitcher.DefaultEngineName = ChakraCoreJsEngine.EngineName;
        using (IJsEngine engine = JsEngineSwitcher.Current.CreateDefaultEngine())
        {
            engine.Execute(jsText);
            string result = engine.CallFunction<string>("say", "chakraCoreJs");
            return Ok(result);
        }

    }
}

3.4 運行效果

三、總結

以上三種框架特性:
● ClearScript、ChakraCore:原始js,支持直接執行腳本字符串
● NodeServices:植入Nodejs對象原則上執行會更快,需要Nodejs語法,只支持執行腳本文件

具體使用哪一種,需要根據業務功能如何實現來定,由於NodeServices不支持讀取字符串且編寫格式有要求,對於某某雲的這種設置效果,猜測是用后面兩種方式,即特定腳本頭+代碼塊設置區域js+特定腳本尾
function dojs(input){
//中間拼接代碼塊的設置,input 獲取入參,output作為出參

return output;
}
最后程序通過調用engine.Script.dojs(表單數據對象);將表單數據傳入腳本作為input入參,並獲取output輸出。

代碼實現(ClearScript方式):
如程序執行js腳本,將用戶對象的手機號中間四位加密

[HttpGet()]
public async Task<IActionResult> Get()
{
    string mobile = "13812345678";
    string jsTextPrefix = "function dojs(input){";   -- 腳本頭
    string jsTextPostfix = "return output;}";		 -- 腳本尾
    //代碼塊設置區域內容
    var jsContent = @"var tel = input.Mobile;           //獲取入參對象
                      var reg=/(\d{3})\d{4}(\d{4})/;	//業務邏輯,如手機號中間四位加密
                      var telnew = tel.replace(reg, '$1****$2');  
                      output = {'Mobile':telnew};       // 定義返回值格式
                      ";
    string jsText = jsTextPrefix + jsContent + jsTextPostfix;
    using (var engine = new V8ScriptEngine())
    {
        engine.DocumentSettings.AccessFlags = Microsoft.ClearScript.DocumentAccessFlags.EnableFileLoading;
        engine.DefaultAccess = Microsoft.ClearScript.ScriptAccess.Full;
        engine.Execute(jsText);  
        //數據對象作為入參
        var input = new
        {
            Mobile = mobile
        };
        var result = engine.Script.dojs(input);  //執行方法
        return Ok(JsonConvert.SerializeObject(result));
    }
}

效果:

git地址:NetCoreExecuteJs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM