将golang代码与基于浏览器的纯JS连接起来

huangapple go评论123阅读模式
英文:

Connecting a golang code with browser based vanilla JS

问题

我正在尝试使用json-rpc将基于浏览器的纯JS与golang代码连接起来,但我一直收到TypeError: Failed to fetch的错误。

这是代码:

Go中的服务器:

  1. package main
  2. import (
  3. "log"
  4. "net"
  5. "net/rpc"
  6. "net/rpc/jsonrpc"
  7. )
  8. type HelloService struct{}
  9. func (p *HelloService) Hello(request string, reply *string) error {
  10. *reply = "Hello " + request
  11. return nil
  12. }
  13. func main() {
  14. rpc.RegisterName("HelloService", new(HelloService))
  15. listener, err := net.Listen("tcp", ":1234")
  16. if err != nil {
  17. log.Fatal("ListenTCP error: ", err)
  18. }
  19. for {
  20. conn, err := listener.Accept()
  21. if err != nil {
  22. log.Fatal("Accept error: ", err)
  23. }
  24. log.Printf("New connection: %+v\n", conn.RemoteAddr())
  25. go jsonrpc.ServeConn(conn)
  26. }
  27. }

浏览器中的客户端 client.html:

  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <p id="demo">Fetch a file to change this text.</p>
  5. <script>
  6. fetch('http://127.0.0.1:1234/rpc/v1', {
  7. method: 'POST',
  8. headers: {
  9. 'Content-Type': 'application/json',
  10. },
  11. body: JSON.stringify({
  12. jsonrpc: '1.0',
  13. method: `HelloService.Hello`,
  14. params: ["popcontent"],//[],
  15. id: "jsonrpc"
  16. })
  17. })
  18. .then((response) => {
  19. if (response.ok) {
  20. return response.json();
  21. }
  22. throw new Error('Something went wrong');
  23. })
  24. .then((responseJson) => {
  25. console.log("app :",error)
  26. })
  27. .catch((error) => {
  28. console.log("app error:",error)
  29. });
  30. </script>
  31. </body>
  32. </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

  1. package main
  2. import (
  3. &quot;log&quot;
  4. &quot;net&quot;
  5. &quot;net/rpc&quot;
  6. &quot;net/rpc/jsonrpc&quot;
  7. )
  8. type HelloService struct{}
  9. func (p *HelloService) Hello(request string, reply *string) error {
  10. *reply = &quot;Hello &quot; + request
  11. return nil
  12. }
  13. func main() {
  14. rpc.RegisterName(&quot;HelloService&quot;, new(HelloService))
  15. listener, err := net.Listen(&quot;tcp&quot;, &quot;:1234&quot;)
  16. if err != nil {
  17. log.Fatal(&quot;ListenTCP error: &quot;, err)
  18. }
  19. for {
  20. conn, err := listener.Accept()
  21. if err != nil {
  22. log.Fatal(&quot;Accept error: &quot;, err)
  23. }
  24. log.Printf(&quot;New connection: %+v\n&quot;, conn.RemoteAddr())
  25. go jsonrpc.ServeConn(conn)
  26. }
  27. }

The client in the browser client.html

`

  1. &lt;!DOCTYPE html&gt;
  2. &lt;html&gt;
  3. &lt;body&gt;
  4. &lt;p id=&quot;demo&quot;&gt;Fetch a file to change this text.&lt;/p&gt;
  5. &lt;script&gt;
  6. fetch(&#39;http://127.0.0.1:1234/rpc/v1&#39;, {
  7. method: &#39;POST&#39;,
  8. headers: {
  9. &#39;Content-Type&#39;: &#39;application/json&#39;,
  10. },
  11. body: JSON.stringify({
  12. jsonrpc: &#39;1.0&#39;,
  13. method: `HelloService.Hello`,
  14. params: [&quot;popcontent&quot;],//[],
  15. id: &quot;jsonrpc&quot;
  16. })
  17. })
  18. .then((response) =&gt; {
  19. if (response.ok) {
  20. return response.json();
  21. }
  22. throw new Error(&#39;Something went wrong&#39;);
  23. })
  24. .then((responseJson) =&gt; {
  25. console.log(&quot;app :&quot;,error)
  26. })
  27. .catch((error) =&gt; {
  28. console.log(&quot;app error:&quot;,error)
  29. });
  30. &lt;/script&gt;
  31. &lt;/body&gt;
  32. &lt;/html&gt;

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

例如

  1. http.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
  2. // 检查
  3. {
  4. // 检查方法
  5. if r.Method != http.MethodPost {
  6. http.Error(w, "不允许的方法", http.StatusMethodNotAllowed)
  7. return
  8. }
  9. // 检查Content-Type
  10. // 检查授权
  11. // ...
  12. }
  13. // 获取用户输入
  14. var req RPCRequest
  15. {
  16. _ = json.NewDecoder(r.Body).Decode(&req)
  17. }
  18. conn, _ := rpc.Dial("tcp", "127.0.0.1:12345")
  19. defer conn.Close()
  20. var reply string
  21. if err := conn.Call(req.Method, req.Params, &reply); err != nil { // <--
  22. http.Error(w, err.Error(), http.StatusBadRequest)
  23. return
  24. }
  25. // 处理响应
  26. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  27. resultBytes, _ := json.Marshal(struct {
  28. Msg string
  29. }{
  30. reply,
  31. })
  32. _, _ = fmt.Fprintln(w, string(resultBytes))
  33. })

可运行的示例

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net"
  7. "net/http"
  8. "net/rpc"
  9. "os/exec"
  10. "runtime"
  11. "strings"
  12. "time"
  13. )
  14. type RPCRequest struct {
  15. Method string
  16. Params json.RawMessage
  17. Id int
  18. }
  19. type Arith int
  20. func (t *Arith) Multiply(args *json.RawMessage, reply *string) error {
  21. para := struct {
  22. A int `json:"a"`
  23. B int `json:"b"`
  24. }{}
  25. if err := json.Unmarshal(*args, &para); err != nil {
  26. return err
  27. }
  28. *reply = fmt.Sprintf("%d", para.A*para.B)
  29. return nil
  30. }
  31. func (t *Arith) unMarshal(args *json.RawMessage) (*struct{ A, B int }, error) {
  32. output := new(struct{ A, B int })
  33. err := json.Unmarshal(*args, output)
  34. return output, err
  35. }
  36. func (t *Arith) Add(args *json.RawMessage, reply *string) error {
  37. para, err := t.unMarshal(args)
  38. if err != nil {
  39. return err
  40. }
  41. *reply = fmt.Sprintf("%d", para.A+para.B)
  42. return nil
  43. }
  44. func NewRPCServer() (listener net.Listener, runFunc func()) {
  45. rpcServer := rpc.NewServer()
  46. // rpcServer.RegisterName("Arith", new(Arith)) // same as below
  47. if err := rpcServer.Register(new(Arith)); err != nil {
  48. log.Fatal(err)
  49. }
  50. listener, _ = net.Listen("tcp", "127.0.0.1:0")
  51. log.Println("[RPC SERVER]", listener.Addr().String())
  52. runFunc = func() {
  53. for {
  54. conn, err := listener.Accept()
  55. if err != nil {
  56. log.Println("[listener.Accept]", err)
  57. return
  58. }
  59. go func(conn net.Conn) {
  60. defer func() {
  61. if err = conn.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
  62. log.Println(err)
  63. }
  64. }()
  65. _ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
  66. rpcServer.ServeConn(conn)
  67. }(conn)
  68. }
  69. }
  70. return listener, runFunc
  71. }
  72. var RPCServerAddr *string
  73. func main() {
  74. listenerRPC, rpcServerRunFunc := NewRPCServer()
  75. defer func() {
  76. _ = listenerRPC.Close()
  77. }()
  78. go rpcServerRunFunc()
  79. RPCServerAddr = new(string)
  80. *RPCServerAddr = listenerRPC.Addr().String()
  81. mux := http.NewServeMux()
  82. mux.HandleFunc("/myrpc/", func(w http.ResponseWriter, r *http.Request) {
  83. if r.Method != http.MethodPost {
  84. http.Error(w, "不允许的方法", http.StatusMethodNotAllowed)
  85. return
  86. }
  87. if r.Header.Get("Content-Type") != "application/json" {
  88. http.Error(w, "内容类型错误", http.StatusBadRequest)
  89. return
  90. }
  91. var req RPCRequest
  92. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  93. http.Error(w, err.Error(), http.StatusBadRequest)
  94. return
  95. }
  96. conn, err := rpc.Dial("tcp", *RPCServerAddr)
  97. if err != nil {
  98. http.Error(w, err.Error(), http.StatusBadRequest)
  99. return
  100. }
  101. defer func() {
  102. _ = conn.Close()
  103. }()
  104. var reply string
  105. if err = conn.Call(req.Method, req.Params, &reply); err != nil {
  106. http.Error(w, err.Error(), http.StatusBadRequest)
  107. return
  108. }
  109. // 处理响应
  110. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  111. resultBytes, _ := json.Marshal(struct {
  112. Msg string
  113. }{
  114. reply,
  115. })
  116. if _, err = fmt.Fprintln(w, string(resultBytes)); err != nil {
  117. log.Println(err)
  118. }
  119. })
  120. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  121. http.ServeFile(w, r, "index.html")
  122. })
  123. server := http.Server{Addr: "127.0.0.1:0", Handler: mux}
  124. listener, _ := net.Listen("tcp", server.Addr)
  125. log.Println("[Addr]:", listener.Addr())
  126. if runtime.GOOS == "windows" {
  127. // 帮助您自动打开浏览器。
  128. go func() {
  129. time.Sleep(1 * time.Second)
  130. if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://"+listener.Addr().String()).Start(); err != nil {
  131. panic(err)
  132. }
  133. }()
  134. }
  135. _ = server.Serve(listener)
  136. }

index.html

  1. <h2>TEST RPC</h2>
  2. <div></div>
  3. <script type="module">
  4. async function rpcCall(method, params) {
  5. return fetch(location.protocol + "//" + location.hostname + (location.port ? ":" + location.port : "") +
  6. "/myrpc/", {
  7. method: 'POST',
  8. headers: {
  9. 'Content-Type': 'application/json'
  10. },
  11. body: JSON.stringify({
  12. method,
  13. params,
  14. id: 0
  15. })
  16. })
  17. .then(response => {
  18. return response.json()
  19. })
  20. .then(result => {
  21. return result
  22. })
  23. .catch(error => {
  24. console.error(error.message)
  25. })
  26. }
  27. const aa = await rpcCall("Arith.Multiply", {a: 2, b: 3})
  28. console.log(aa);
  29. // more test
  30. [
  31. ["Arith.Multiply", {a: 2, b: 3}],
  32. ["Arith.Add", {a: 2, b: 3}],
  33. ].forEach(async ([method, params]) => {
  34. const val = await rpcCall(method, params)
  35. document.querySelector("div").innerHTML += JSON.stringify(val, null, 2) + "<br>"
  36. })
  37. </script>
英文:

Create a Post and, when a certain condition is met, initiate a TCP connection and feed the data into conn.Call.

for example

  1. http.HandleFunc(&quot;/myrpc/&quot;, func(w http.ResponseWriter, r *http.Request) {
  2. // check
  3. {
  4. // check Method
  5. if r.Method != http.MethodPost {
  6. http.Error(w, &quot;Method not allowed&quot;, http.StatusMethodNotAllowed)
  7. return
  8. }
  9. // check Content-Type
  10. // check Auth
  11. // ...
  12. }
  13. // Get user input
  14. var req RPCRequest
  15. {
  16. _ = json.NewDecoder(r.Body).Decode(&amp;req)
  17. }
  18. conn, _ := rpc.Dial(&quot;tcp&quot;, &quot;127.0.0.1:12345&quot;)
  19. defer conn.Close()
  20. var reply string
  21. if err := conn.Call(req.Method, req.Params, &amp;reply); err != nil { // &lt;--
  22. http.Error(w, err.Error(), http.StatusBadRequest)
  23. return
  24. }
  25. // handle response
  26. w.Header().Set(&quot;Content-Type&quot;, &quot;application/json; charset=utf-8&quot;)
  27. resultBytes, _ := json.Marshal(struct {
  28. Msg string
  29. }{
  30. reply,
  31. })
  32. _, _ = fmt.Fprintln(w, string(resultBytes))
  33. })

A Runable example

  1. package main
  2. import (
  3. &quot;encoding/json&quot;
  4. &quot;fmt&quot;
  5. &quot;log&quot;
  6. &quot;net&quot;
  7. &quot;net/http&quot;
  8. &quot;net/rpc&quot;
  9. &quot;os/exec&quot;
  10. &quot;runtime&quot;
  11. &quot;strings&quot;
  12. &quot;time&quot;
  13. )
  14. type RPCRequest struct {
  15. Method string
  16. Params json.RawMessage
  17. Id int
  18. }
  19. type Arith int
  20. func (t *Arith) Multiply(args *json.RawMessage, reply *string) error {
  21. para := struct {
  22. A int `json:&quot;a&quot;`
  23. B int `json:&quot;b&quot;`
  24. }{}
  25. if err := json.Unmarshal(*args, &amp;para); err != nil {
  26. return err
  27. }
  28. *reply = fmt.Sprintf(&quot;%d&quot;, para.A*para.B)
  29. return nil
  30. }
  31. func (t *Arith) unMarshal(args *json.RawMessage) (*struct{ A, B int }, error) {
  32. output := new(struct{ A, B int })
  33. err := json.Unmarshal(*args, output)
  34. return output, err
  35. }
  36. func (t *Arith) Add(args *json.RawMessage, reply *string) error {
  37. para, err := t.unMarshal(args)
  38. if err != nil {
  39. return err
  40. }
  41. *reply = fmt.Sprintf(&quot;%d&quot;, para.A+para.B)
  42. return nil
  43. }
  44. func NewRPCServer() (listener net.Listener, runFunc func()) {
  45. rpcServer := rpc.NewServer()
  46. // rpcServer.RegisterName(&quot;Arith&quot;, new(Arith)) // same as below
  47. if err := rpcServer.Register(new(Arith)); err != nil {
  48. log.Fatal(err)
  49. }
  50. listener, _ = net.Listen(&quot;tcp&quot;, &quot;127.0.0.1:0&quot;)
  51. log.Println(&quot;[RPC SERVER]&quot;, listener.Addr().String())
  52. runFunc = func() {
  53. for {
  54. conn, err := listener.Accept()
  55. if err != nil {
  56. log.Println(&quot;[listener.Accept]&quot;, err)
  57. return
  58. }
  59. go func(conn net.Conn) {
  60. defer func() {
  61. if err = conn.Close(); err != nil &amp;&amp; !strings.Contains(err.Error(), &quot;use of closed network connection&quot;) {
  62. log.Println(err)
  63. }
  64. }()
  65. _ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
  66. rpcServer.ServeConn(conn)
  67. }(conn)
  68. }
  69. }
  70. return listener, runFunc
  71. }
  72. var RPCServerAddr *string
  73. func main() {
  74. listenerRPC, rpcServerRunFunc := NewRPCServer()
  75. defer func() {
  76. _ = listenerRPC.Close()
  77. }()
  78. go rpcServerRunFunc()
  79. RPCServerAddr = new(string)
  80. *RPCServerAddr = listenerRPC.Addr().String()
  81. mux := http.NewServeMux()
  82. mux.HandleFunc(&quot;/myrpc/&quot;, func(w http.ResponseWriter, r *http.Request) {
  83. if r.Method != http.MethodPost {
  84. http.Error(w, &quot;Method not allowed&quot;, http.StatusMethodNotAllowed)
  85. return
  86. }
  87. if r.Header.Get(&quot;Content-Type&quot;) != &quot;application/json&quot; {
  88. http.Error(w, &quot;content type error&quot;, http.StatusBadRequest)
  89. return
  90. }
  91. var req RPCRequest
  92. if err := json.NewDecoder(r.Body).Decode(&amp;req); err != nil {
  93. http.Error(w, err.Error(), http.StatusBadRequest)
  94. return
  95. }
  96. conn, err := rpc.Dial(&quot;tcp&quot;, *RPCServerAddr)
  97. if err != nil {
  98. http.Error(w, err.Error(), http.StatusBadRequest)
  99. return
  100. }
  101. defer func() {
  102. _ = conn.Close()
  103. }()
  104. var reply string
  105. if err = conn.Call(req.Method, req.Params, &amp;reply); err != nil {
  106. http.Error(w, err.Error(), http.StatusBadRequest)
  107. return
  108. }
  109. // handle response
  110. w.Header().Set(&quot;Content-Type&quot;, &quot;application/json; charset=utf-8&quot;)
  111. resultBytes, _ := json.Marshal(struct {
  112. Msg string
  113. }{
  114. reply,
  115. })
  116. if _, err = fmt.Fprintln(w, string(resultBytes)); err != nil {
  117. log.Println(err)
  118. }
  119. })
  120. mux.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
  121. http.ServeFile(w, r, &quot;index.html&quot;)
  122. })
  123. server := http.Server{Addr: &quot;127.0.0.1:0&quot;, Handler: mux}
  124. listener, _ := net.Listen(&quot;tcp&quot;, server.Addr)
  125. log.Println(&quot;[Addr]:&quot;, listener.Addr())
  126. if runtime.GOOS == &quot;windows&quot; {
  127. // help you open the browser automatically.
  128. go func() {
  129. time.Sleep(1 * time.Second)
  130. if err := exec.Command(&quot;rundll32&quot;, &quot;url.dll,FileProtocolHandler&quot;, &quot;http://&quot;+listener.Addr().String()).Start(); err != nil {
  131. panic(err)
  132. }
  133. }()
  134. }
  135. _ = server.Serve(listener)
  136. }

index.html

  1. &lt;h2&gt;TEST RPC&lt;/h2&gt;
  2. &lt;div&gt;&lt;/div&gt;
  3. &lt;script type=&quot;module&quot;&gt;
  4. async function rpcCall(method, params) {
  5. return fetch(location.protocol + &quot;//&quot; + location.hostname + (location.port ? &quot;:&quot; + location.port : &quot;&quot;) +
  6. &quot;/myrpc/&quot;, {
  7. method: &#39;POST&#39;,
  8. headers: {
  9. &#39;Content-Type&#39;: &#39;application/json&#39;
  10. },
  11. body: JSON.stringify({
  12. method,
  13. params,
  14. id: 0
  15. })
  16. })
  17. .then(response =&gt; {
  18. return response.json()
  19. })
  20. .then(result =&gt; {
  21. return result
  22. })
  23. .catch(error =&gt; {
  24. console.error(error.message)
  25. })
  26. }
  27. const aa = await rpcCall(Arith.Multiply&quot;, {a: 2, b: 3})
  28. console.log(aa);
  29. // more test
  30. [
  31. [&quot;Arith.Multiply&quot;, {a: 2, b: 3}],
  32. [&quot;Arith.Add&quot;, {a: 2, b: 3}],
  33. ].forEach(async ([method, params]) =&gt; {
  34. const val = await rpcCall(method, params)
  35. document.querySelector(&quot;div&quot;).innerHTML += JSON.stringify(val, null, 2) + &quot;&lt;br&gt;&quot;
  36. })
  37. &lt;/script&gt;

huangapple
  • 本文由 发表于 2022年6月22日 02:17:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/72705456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定