英文:
Connecting a golang code with browser based vanilla JS
问题
我正在尝试使用json-rpc将基于浏览器的纯JS与golang代码连接起来,但我一直收到TypeError: Failed to fetch
的错误。
这是代码:
Go中的服务器:
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type HelloService struct{}
func (p *HelloService) Hello(request string, reply *string) error {
*reply = "Hello " + request
return nil
}
func main() {
rpc.RegisterName("HelloService", new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error: ", err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error: ", err)
}
log.Printf("New connection: %+v\n", conn.RemoteAddr())
go jsonrpc.ServeConn(conn)
}
}
浏览器中的客户端 client.html:
<!DOCTYPE html>
<html>
<body>
<p id="demo">Fetch a file to change this text.</p>
<script>
fetch('http://127.0.0.1:1234/rpc/v1', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '1.0',
method: `HelloService.Hello`,
params: ["popcontent"],//[],
id: "jsonrpc"
})
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error('Something went wrong');
})
.then((responseJson) => {
console.log("app :",error)
})
.catch((error) => {
console.log("app error:",error)
});
</script>
</body>
</html>
我能够在本地连接JS代码和Go代码吗?
如果我可以在本地运行两者,我该如何修复代码?
英文:
I am trying to connect a golang code with browser based vanilla JS in the browser using json-rpc yet I keep getting TypeError: Failed to fetch
Here is the code
The server in go
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type HelloService struct{}
func (p *HelloService) Hello(request string, reply *string) error {
*reply = "Hello " + request
return nil
}
func main() {
rpc.RegisterName("HelloService", new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error: ", err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error: ", err)
}
log.Printf("New connection: %+v\n", conn.RemoteAddr())
go jsonrpc.ServeConn(conn)
}
}
The client in the browser client.html
`
<!DOCTYPE html>
<html>
<body>
<p id="demo">Fetch a file to change this text.</p>
<script>
fetch('http://127.0.0.1:1234/rpc/v1', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '1.0',
method: `HelloService.Hello`,
params: ["popcontent"],//[],
id: "jsonrpc"
})
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error('Something went wrong');
})
.then((responseJson) => {
console.log("app :",error)
})
.catch((error) => {
console.log("app error:",error)
});
</script>
</body>
</html>
Can I even connect JS code with go code locally?
If I can run both locally, how can I fix the code?
答案1
得分: 1
创建一个帖子,并在满足特定条件时建立一个TCP连接,并将数据传递给conn.Call
。
例如
http.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
// 检查
{
// 检查方法
if r.Method != http.MethodPost {
http.Error(w, "不允许的方法", http.StatusMethodNotAllowed)
return
}
// 检查Content-Type
// 检查授权
// ...
}
// 获取用户输入
var req RPCRequest
{
_ = json.NewDecoder(r.Body).Decode(&req)
}
conn, _ := rpc.Dial("tcp", "127.0.0.1:12345")
defer conn.Close()
var reply string
if err := conn.Call(req.Method, req.Params, &reply); err != nil { // <--
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 处理响应
w.Header().Set("Content-Type", "application/json; charset=utf-8")
resultBytes, _ := json.Marshal(struct {
Msg string
}{
reply,
})
_, _ = fmt.Fprintln(w, string(resultBytes))
})
可运行的示例
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"os/exec"
"runtime"
"strings"
"time"
)
type RPCRequest struct {
Method string
Params json.RawMessage
Id int
}
type Arith int
func (t *Arith) Multiply(args *json.RawMessage, reply *string) error {
para := struct {
A int `json:"a"`
B int `json:"b"`
}{}
if err := json.Unmarshal(*args, ¶); err != nil {
return err
}
*reply = fmt.Sprintf("%d", para.A*para.B)
return nil
}
func (t *Arith) unMarshal(args *json.RawMessage) (*struct{ A, B int }, error) {
output := new(struct{ A, B int })
err := json.Unmarshal(*args, output)
return output, err
}
func (t *Arith) Add(args *json.RawMessage, reply *string) error {
para, err := t.unMarshal(args)
if err != nil {
return err
}
*reply = fmt.Sprintf("%d", para.A+para.B)
return nil
}
func NewRPCServer() (listener net.Listener, runFunc func()) {
rpcServer := rpc.NewServer()
// rpcServer.RegisterName("Arith", new(Arith)) // same as below
if err := rpcServer.Register(new(Arith)); err != nil {
log.Fatal(err)
}
listener, _ = net.Listen("tcp", "127.0.0.1:0")
log.Println("[RPC SERVER]", listener.Addr().String())
runFunc = func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Println("[listener.Accept]", err)
return
}
go func(conn net.Conn) {
defer func() {
if err = conn.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Println(err)
}
}()
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
rpcServer.ServeConn(conn)
}(conn)
}
}
return listener, runFunc
}
var RPCServerAddr *string
func main() {
listenerRPC, rpcServerRunFunc := NewRPCServer()
defer func() {
_ = listenerRPC.Close()
}()
go rpcServerRunFunc()
RPCServerAddr = new(string)
*RPCServerAddr = listenerRPC.Addr().String()
mux := http.NewServeMux()
mux.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "不允许的方法", http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Content-Type") != "application/json" {
http.Error(w, "内容类型错误", http.StatusBadRequest)
return
}
var req RPCRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
conn, err := rpc.Dial("tcp", *RPCServerAddr)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer func() {
_ = conn.Close()
}()
var reply string
if err = conn.Call(req.Method, req.Params, &reply); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 处理响应
w.Header().Set("Content-Type", "application/json; charset=utf-8")
resultBytes, _ := json.Marshal(struct {
Msg string
}{
reply,
})
if _, err = fmt.Fprintln(w, string(resultBytes)); err != nil {
log.Println(err)
}
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
server := http.Server{Addr: "127.0.0.1:0", Handler: mux}
listener, _ := net.Listen("tcp", server.Addr)
log.Println("[Addr]:", listener.Addr())
if runtime.GOOS == "windows" {
// 帮助您自动打开浏览器。
go func() {
time.Sleep(1 * time.Second)
if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://"+listener.Addr().String()).Start(); err != nil {
panic(err)
}
}()
}
_ = server.Serve(listener)
}
index.html
<h2>TEST RPC</h2>
<div></div>
<script type="module">
async function rpcCall(method, params) {
return fetch(location.protocol + "//" + location.hostname + (location.port ? ":" + location.port : "") +
"/myrpc/", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
method,
params,
id: 0
})
})
.then(response => {
return response.json()
})
.then(result => {
return result
})
.catch(error => {
console.error(error.message)
})
}
const aa = await rpcCall("Arith.Multiply", {a: 2, b: 3})
console.log(aa);
// more test
[
["Arith.Multiply", {a: 2, b: 3}],
["Arith.Add", {a: 2, b: 3}],
].forEach(async ([method, params]) => {
const val = await rpcCall(method, params)
document.querySelector("div").innerHTML += JSON.stringify(val, null, 2) + "<br>"
})
</script>
英文:
Create a Post and, when a certain condition is met, initiate a TCP connection and feed the data into conn.Call
.
for example
http.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
// check
{
// check Method
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// check Content-Type
// check Auth
// ...
}
// Get user input
var req RPCRequest
{
_ = json.NewDecoder(r.Body).Decode(&req)
}
conn, _ := rpc.Dial("tcp", "127.0.0.1:12345")
defer conn.Close()
var reply string
if err := conn.Call(req.Method, req.Params, &reply); err != nil { // <--
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// handle response
w.Header().Set("Content-Type", "application/json; charset=utf-8")
resultBytes, _ := json.Marshal(struct {
Msg string
}{
reply,
})
_, _ = fmt.Fprintln(w, string(resultBytes))
})
A Runable example
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"os/exec"
"runtime"
"strings"
"time"
)
type RPCRequest struct {
Method string
Params json.RawMessage
Id int
}
type Arith int
func (t *Arith) Multiply(args *json.RawMessage, reply *string) error {
para := struct {
A int `json:"a"`
B int `json:"b"`
}{}
if err := json.Unmarshal(*args, &para); err != nil {
return err
}
*reply = fmt.Sprintf("%d", para.A*para.B)
return nil
}
func (t *Arith) unMarshal(args *json.RawMessage) (*struct{ A, B int }, error) {
output := new(struct{ A, B int })
err := json.Unmarshal(*args, output)
return output, err
}
func (t *Arith) Add(args *json.RawMessage, reply *string) error {
para, err := t.unMarshal(args)
if err != nil {
return err
}
*reply = fmt.Sprintf("%d", para.A+para.B)
return nil
}
func NewRPCServer() (listener net.Listener, runFunc func()) {
rpcServer := rpc.NewServer()
// rpcServer.RegisterName("Arith", new(Arith)) // same as below
if err := rpcServer.Register(new(Arith)); err != nil {
log.Fatal(err)
}
listener, _ = net.Listen("tcp", "127.0.0.1:0")
log.Println("[RPC SERVER]", listener.Addr().String())
runFunc = func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Println("[listener.Accept]", err)
return
}
go func(conn net.Conn) {
defer func() {
if err = conn.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Println(err)
}
}()
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
rpcServer.ServeConn(conn)
}(conn)
}
}
return listener, runFunc
}
var RPCServerAddr *string
func main() {
listenerRPC, rpcServerRunFunc := NewRPCServer()
defer func() {
_ = listenerRPC.Close()
}()
go rpcServerRunFunc()
RPCServerAddr = new(string)
*RPCServerAddr = listenerRPC.Addr().String()
mux := http.NewServeMux()
mux.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Content-Type") != "application/json" {
http.Error(w, "content type error", http.StatusBadRequest)
return
}
var req RPCRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
conn, err := rpc.Dial("tcp", *RPCServerAddr)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer func() {
_ = conn.Close()
}()
var reply string
if err = conn.Call(req.Method, req.Params, &reply); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// handle response
w.Header().Set("Content-Type", "application/json; charset=utf-8")
resultBytes, _ := json.Marshal(struct {
Msg string
}{
reply,
})
if _, err = fmt.Fprintln(w, string(resultBytes)); err != nil {
log.Println(err)
}
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
server := http.Server{Addr: "127.0.0.1:0", Handler: mux}
listener, _ := net.Listen("tcp", server.Addr)
log.Println("[Addr]:", listener.Addr())
if runtime.GOOS == "windows" {
// help you open the browser automatically.
go func() {
time.Sleep(1 * time.Second)
if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://"+listener.Addr().String()).Start(); err != nil {
panic(err)
}
}()
}
_ = server.Serve(listener)
}
index.html
<h2>TEST RPC</h2>
<div></div>
<script type="module">
async function rpcCall(method, params) {
return fetch(location.protocol + "//" + location.hostname + (location.port ? ":" + location.port : "") +
"/myrpc/", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
method,
params,
id: 0
})
})
.then(response => {
return response.json()
})
.then(result => {
return result
})
.catch(error => {
console.error(error.message)
})
}
const aa = await rpcCall(Arith.Multiply", {a: 2, b: 3})
console.log(aa);
// more test
[
["Arith.Multiply", {a: 2, b: 3}],
["Arith.Add", {a: 2, b: 3}],
].forEach(async ([method, params]) => {
const val = await rpcCall(method, params)
document.querySelector("div").innerHTML += JSON.stringify(val, null, 2) + "<br>"
})
</script>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论