概要
今まではコンソールから入力するだけでしたが、いろいろ機能を追加してテストするときJavaScriptの入力が大変になってきます。load
関数はありますが。そこで、今回はWebインターフェースを追加して楽に開発しましょう。Webポートを開いてサーバー側のJavaScriptが実行できるのでセキュリティに十分配慮してください。
の知識を前提としています。
実装
Webインターフェースを実装する前の準備
今まで作成したプログラムの一部をWebインターフェースに合わせて変更します。
jsRuntime
にout io.Writer
を追加します。これはjsprint
の出力先がコンソールかブラウザかを決めるためです。
type jsRuntime struct {
runtime *goja.Runtime
stringify goja.Callable
program *goja.Program
out io.Writer //<==追加
}
func initialSetting
の一部を変更します。
//変更前
js := &jsRuntime{runtime: rt}
//変更後
js := &jsRuntime{runtime: rt, out: os.Stdout}
さらに、jsprint
関数内で fmt.Fprintf
に変更します。//web
がついているところが変更箇所です。
func (js *jsRuntime) jsprint(vals ...goja.Value) {
rt := js.runtime
format := "%v"
for _, val := range vals {
str, ok := val.Export().(string)
if ok {
fmt.Fprintf(js.out, format, str) //web
format = " %v"
continue
}
v, err := js.stringify(goja.Undefined(), val)
if err != nil {
rt.Interrupt(rt.NewGoError(err))
return
}
fmt.Fprintf(js.out, format, v) //web
format = " %v"
}
fmt.Fprintln(js.out) //web
}
Webインターフェースの実装
まず,func initialSetting
でJavaScriptにGo言語のWebインターフェースの関数をセットします。
rt.Set("web", js.execWebServer)
続いてexecWebServer
や関連する関数です.
import (
"bytes"
"fmt"
"net/http"
"os"
)
var isExecWeb = false
func (js *jsRuntime) execWebServer(port int) {
if isExecWeb {
js.runtime.Interrupt("already executed")
return
}
isExecWeb = true
go js.webServer(port)
}
func (js *jsRuntime) webServer(port int) {
http.Handle("/html/", http.StripPrefix("/html/", http.FileServer(http.Dir("html/"))))
http.HandleFunc("/script", js.routeScript)
http.ListenAndServe(fmt.Sprintf("localhost:%d", port), nil)
}
func (js *jsRuntime) routeScript(w http.ResponseWriter, r *http.Request) {
js.out = w
defer func() { js.out = os.Stdout }()
bufferBody := new(bytes.Buffer)
bufferBody.ReadFrom(r.Body)
reqBody := bufferBody.String()
val, err := js.runtime.RunScript("web", reqBody)
if err == nil {
js.jsprint(val)
} else {
js.jsprint(js.runtime.ToValue(fmt.Sprintf("%v\n", err)))
}
}
JavaScriptからの引数はポート番号です。 カレントディレクトリの下にhtml
というディレクトリを作ってそこに必要なHTMLを置きます。JavaScriptを実行させるURLは/script
としました。
js.out = w
でJavaScriptの表示がブラウザになります。処理終了時にはos.Stdout
に戻します。これでコンソールからもWebからも入力できます。ただし、JavaScriptのRuntime
は共有していますので、同時には実行しないでください。Webインターフェース同士の同時処理もしないように注意が必要です。
続いてHTMLです。ファイル名はscript.html
としてhtml
ディレクトリに置きます。また、jQuery
を使用しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>JavaScript Shell</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<form>
<textarea cols="60" rows="5" id="query" spellcheck="false" wrap="off"></textarea>
</form>
<button id="ajax" onclick="postAjax()">run</button>
<div id="output"></div>
<script>
function postAjax() {
sendData = $('#query').val()
$.ajax({
url: '/script',
type: 'POST',
data: sendData
}).done((data) => {
ajaxSuccess(data);
}).fail((data) => {
ajaxFail(data);
});
}
function ajaxSuccess(result) {
document.getElementById("output").innerHTML = `<pre>${result}</pre>`
}
function ajaxFail(result) {
document.getElementById("output").innerHTML = result
}
</script>
</body>
</html>
実行
go run main.go
> web(8080)
undefined
>
ブラウザで実行した結果です
次のJavaScriptでHTMLを生成してそれをブラウザで表示させます。残念ながらHTML
をエンコードする関数は用意していないのでそのまま出力します。
document.getElementById("output").innerHTML = `<pre>${result}</pre>`
を次のように変更してHTMLを有効にします。
document.getElementById("output").innerHTML = result
下記のJavaScriptを実行しています。最後の""
はundefined
がでないように戻り値を空文字にしています。一度作成したScriptはファイルにしてブラウザからはload
で実行することもできます。
print ("<table border=1><th>A</th><th>B</th><th>C</th></tr>")
var asum = 0
var bsum = 0
excel("Book1.xlsx", "Sheet1").ForEachArray(function(ix, row) {
print("<tr>")
asum += row[0]
bsum += row[1]
row.forEach(function(cell){
print("<td>", cell, "</td>")
})
print("</tr>")
})
print("<tr><td>",asum, "</tb><td>", bsum ,"</td><td>合計</td></tr>")
""
2次元データの文字列にしてデータだけブラウザに返しブラウザ側でeval
を実行、それからJavaScriptのデータとしてブラウザ側で処理することも可能でしょう。
まとめ
これで開発も楽になります。また、大量の出力データもコンソールよりはブラウザの方が見やすいです。
Web機能は実行できましたがWeb機能だけ止める方法はよくわかりませんでした。それらしいパッケージはありましたが試していません。なのでWeb機能を停止したいときは一度プログラムを終了してから再起動してください。これで十分だと思っています。