JavaScript 中使用fetch的AbortController和取消功能:如何在Go 實現

更新 發佈閱讀 13 分鐘
raw-image


有多種情況下我們需要從API取消請求:

  1. 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。
  2. 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。
  3. 訂閱取消。

......等等

這將對對這個系統沒有信心的使用者產生影響。因此,開發者需要認真對待這個問題。


我使用Vue和Go建立了一個簡單的前端系統和後端系統的示範。


後端部分

mkdir cancellation-demo && cd cancellation-demo

建立資料夾,稱為 `cancellation-demo`

go mod init callcellation-demo && \ 
touch main.go && \
go get github.com/gin-gonic/gin

從go module和main.go開始。導入gin套件。

以下是main.go的內容。建立一個簡單的Hello World。

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func CancelHandler(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello world")
}

func main() {
router := gin.Default()
router.GET("/", EchoHandler)
router.Run()
}

我們的目標是在收到請求後等待3秒,然後發送一個文字回應"Hello World"。如果使用者在3秒內取消了請求,我們將停止處理並打印結果,告知系統管理員該請求已被取消

以下範例

func CancelHandler(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello world")
}

我們改寫成

func CancelHandler(ctx *gin.Context) {
responseResult := make(chan string)

go func() {
time.Sleep(3 * time.Second)
responseResult <- "hello world"
}()

for {
select {
case result := <-responseResult:
ctx.String(http.StatusOK, result)
return
case <-ctx.Request.Context().Done():
log.Println("abort triggerred.Cancellation happen")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}
}

讓我來解釋這部分:

我們需要建立一個goroutine和channel來傳送資料,並使用CancelHandler接收channel和ctx.Context().Done()來傳送chan類型。

這有助於我們檢查是否在請求被取消時收到中止訊號。這個方法將由中止訊號觸發。

因為我們需要建立一個在不同端口上監聽的前端專案,這導致了跨來源資源共享(CORS)的問題。因此,我們需要建立一個中間件(Middleware)來解決這個問題。

func CorsMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Writer.Header().Set("Access-Control-allow-Credentials", "true")
ctx.Writer.Header().Set("Access-Control-allow-Method", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-allow-Headers", "Authorization, Accept,Accept-Encoding, Content-Type,Content-Length, Cache-Control, Origin, X-CSRF-Token, X-Requested-With")
if ctx.Request.Method == http.MethodOptions {
ctx.AbortWithStatus(http.StatusNoContent)
 return
}
ctx.Next()
}
}

最後,我們的後端專案的部分就會看起來像這樣

package main

import (
"log"
"net/http"
"time"

"github.com/gin-gonic/gin"
)

func CorsMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Writer.Header().Set("Access-Control-allow-Credentials", "true")
ctx.Writer.Header().Set("Access-Control-allow-Method", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-allow-Headers", "Authorization, Accept,Accept-Encoding, Content-Type,Content-Length, Cache-Control, Origin, X-CSRF-Token, X-Requested-With")
if ctx.Request.Method == http.MethodOptions {
ctx.AbortWithStatus(http.StatusNoContent)
return
}
ctx.Next()
}
}

func CancelHandler(ctx *gin.Context) {
responseResult := make(chan string)

go func() {
time.Sleep(3 * time.Second)
responseResult <- "hello world"
}()

for {
select {
case result := <-responseResult:
ctx.String(http.StatusOK, result)
return
case <-ctx.Request.Context().Done():
log.Println("abort triggered")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}

}

func main() {
router := gin.Default()
router.Use(CorsMiddleware())
router.GET("/", CancelHandler)
router.Run()
}

現在換前端的部分

我們前端使用 vue 作為我們的前端框架

pnpm create vite --template vue-ts web && \
cd web && \
pnpm install

注意:對了,我本人個人是喜歡用 pnpm 作為我的 package manager,
不代表不能用 npm

然後我們將建立一個簡單的使用AbortController的fetch範例。這部分是向http://localhost:8080發送請求的部分。切記我們的後端專案在端口8080上監聽。

import { ref } from 'vue';
const abortReference = ref(new AbortController());

const getRequest = async () => {
  abortReference.value = new AbortController();
  try {
   const response = await fetch('http://localhost:8080', {
       method: 'GET',
      signal: abortReference.value.signal,
  })
   const mainContent = await response.text();
    console.log(mainContent);
  } catch{
   console.error('api error');
  }
}

取消功能的部分

const quitRequest = () => {
console.log('cancel request');
abortReference.value.abort();
}

現在我們把兩個方法取代原本專案的 app.vue

<script setup lang="ts">
import { ref } from 'vue';
const abortReference = ref(new AbortController());

const getRequest = async () => {
abortReference.value = new AbortController();
  try {
  const response = await fetch('http://localhost:8080', {
    method: 'GET',
     signal: abortReference.value.signal,
})
  const mainContent = await response.text();
  console.log(mainContent);
} catch{
  console.error('api error');
}
}

const quitRequest = () => {
console.log('cancel request');
abortReference.value.abort();
}

</script>

<template>
<button @click="getRequest">
click to request
</button>
<button @click="quitRequest">
Abort Request
</button>
</template>

這樣就大功告成了,

pnpm dev

然後打開瀏覽器我們會看到的結果

raw-image

如果有任何問題歡迎跟我討論,感謝你得閱讀

以下是我的 medium 帳號

還有我發表的英文版原文 ,如果對我的創作有興趣歡迎追蹤捧場

留言
avatar-img
Michael 的程式平台
3會員
5內容數
如果對網頁開發,區塊鏈開發,手機應用開發,桌面應用開發有興趣的可以來看看
2024/06/29
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
2024/06/29
前言 從零開始構建一個 DateTimePicker 可能看起來令人畏懼,但試想一下你將獲得的靈活性和控制力。在這個系列中,我們將逐步揭開構建過程的神秘面紗,讓您能夠創建一個完全符合需求的自定義 DateTimePicker。 本文章,屬於付費系列的文章,這篇文章,我會希望讀者可以得到的
Thumbnail
2024/06/15
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
Thumbnail
2024/06/15
# 簡介 身為一位專注於 Vue.js 的前端開發者,這是我第一次嘗試構建 Flutter 網頁應用。讓我們開始吧! ## 第一次嘗試 ### 第一步:創建一個 Flutter 應用 首先,通過運行以下命令來創建一個新的 Flutter 項目: ```sh flutter
Thumbnail
2024/01/14
我看完影片只有一個想法,台灣目前還是用傳統方式開票還有利用人力的方式進行, 我認為這樣並不是很對,因為會增加人為疏失的可能性以外,還有可能造成做票或是小動作的嫌疑,我認為即便有人監票,那種很會『變魔術』的人,也會讓監票員造成困難 我身為一名軟體工程師,是要想 solution 的,
Thumbnail
2024/01/14
我看完影片只有一個想法,台灣目前還是用傳統方式開票還有利用人力的方式進行, 我認為這樣並不是很對,因為會增加人為疏失的可能性以外,還有可能造成做票或是小動作的嫌疑,我認為即便有人監票,那種很會『變魔術』的人,也會讓監票員造成困難 我身為一名軟體工程師,是要想 solution 的,
Thumbnail
看更多
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
微前端是一種現代前端架構,旨在將大型前端應用拆分為獨立、可獨立部署的小模組。這與微服務在後端的策略相似。 本文將探討微前端的基本概念,以及如何在基於Gin的後端系統中實施它,從而實現真正的全棧模組化。
Thumbnail
微前端是一種現代前端架構,旨在將大型前端應用拆分為獨立、可獨立部署的小模組。這與微服務在後端的策略相似。 本文將探討微前端的基本概念,以及如何在基於Gin的後端系統中實施它,從而實現真正的全棧模組化。
Thumbnail
RESTful Web 服務是現代 Web 和移動應用的核心,它們提供了一個標準化的方式來互動和交換數據。使用Gin構建這些服務是一種流行選擇,還記得之前提過的 嗎?本文將介紹一些最佳實踐,幫助你創建高效和可維護的RESTful服務。
Thumbnail
RESTful Web 服務是現代 Web 和移動應用的核心,它們提供了一個標準化的方式來互動和交換數據。使用Gin構建這些服務是一種流行選擇,還記得之前提過的 嗎?本文將介紹一些最佳實踐,幫助你創建高效和可維護的RESTful服務。
Thumbnail
微服務架構是一種把應用程序劃分成一系列小的、獨立的服務,每個服務都運行在其自己的進程中,並與其他服務通過 API 或 RPC 通信。在這篇文章中,我們將使用 Gin 作為我們的 HTTP 框架,並使用 gRPC 來實現服務間的通信。
Thumbnail
微服務架構是一種把應用程序劃分成一系列小的、獨立的服務,每個服務都運行在其自己的進程中,並與其他服務通過 API 或 RPC 通信。在這篇文章中,我們將使用 Gin 作為我們的 HTTP 框架,並使用 gRPC 來實現服務間的通信。
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
嗨,各位開發者!現代 Web 開發越來越傾向於前後端分離,使得前端框架和後端框架的協同工作變得非常重要。在這篇文章中,我們將專注於兩個極受歡迎的框架 —— Gin 和 Vue.js,探討如何將它們整合起來,為你的用戶提供流暢的Web 體驗。
Thumbnail
嗨,各位開發者!現代 Web 開發越來越傾向於前後端分離,使得前端框架和後端框架的協同工作變得非常重要。在這篇文章中,我們將專注於兩個極受歡迎的框架 —— Gin 和 Vue.js,探討如何將它們整合起來,為你的用戶提供流暢的Web 體驗。
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
Gin框架深入解析:路由和處理器 Hey 朋友們!上回我們介紹了 Gin 框架的基礎知識,今天我們要更深入一些,看看 Gin 如何處理路由和相對應的處理器吧! Gin 的路由機制非常靈活和高效,可以快速地配置不同的路由方式。再配合上處理器函數,讓我們可以有針對性地處理不同路由的請求。探索完今
Thumbnail
Gin框架深入解析:路由和處理器 Hey 朋友們!上回我們介紹了 Gin 框架的基礎知識,今天我們要更深入一些,看看 Gin 如何處理路由和相對應的處理器吧! Gin 的路由機制非常靈活和高效,可以快速地配置不同的路由方式。再配合上處理器函數,讓我們可以有針對性地處理不同路由的請求。探索完今
Thumbnail
嗨各位!歡迎來到這個系列,今天我們要探索的是 Gin 框架!如果你對於開發高效且輕量級的 Web 應用感興趣,那你絕對不能錯過這篇!
Thumbnail
嗨各位!歡迎來到這個系列,今天我們要探索的是 Gin 框架!如果你對於開發高效且輕量級的 Web 應用感興趣,那你絕對不能錯過這篇!
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
Thumbnail
有多種情況下我們需要從API取消請求: 如果我們正在建立一個支付系統,並且請求一直進行,但使用者希望立即取消這筆訂單。這就是為什麼開發者會建立這個功能的原因。 從電子商務購買商品。使用者意外關閉了頁面,無法立即返回上一頁。 訂閱取消。 ......等等 這將對對這個系統沒有信心的使用者產生影響。因
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News