iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Build on Google AI

單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?系列 第 17

Day 17 | 前後端串接:透過 Axios 呼叫 Cloud Run,並用 AI 跨端排查 Guardrail 障礙

  • 分享至 

  • xImage
  •  

前言:雙A連線,讓手機 APP 與雲端 API 連線

經過前面的努力,目前手機 APP (React Native + Expo) 已經建立好跑者的個人設定與課表介面,而我們的後端大腦 (Spring Boot + Gemini) 也已經安穩地部署在 GCP Cloud Run 上。
今天,我們要搭建跨越這兩端的橋樑:前後端 API 串接
我們選擇在使用度高的 Axios 作為 HTTP 請求工具,並透過實體手機上的 Expo Go 進行真機測試。

觀念轉換:React Native 與實體手機串接 API 的冷知識

在請 AI 幫忙寫串接程式碼之前,有兩個開發 React Native 專屬的網路觀念必須先掌握:

1. 不用擔心 CORS 跨域問題

以前寫 Web 前端呼叫不同網域的 API 時,最怕遇到紅字 CORS policy blocked。但在 React Native 開發原生 APP 時,網路請求是直接走手機原生的網路層(iOS 的 NSURLSession / Android 的 OkHttp),完全沒有瀏覽器的安全沙盒限制,因此不會有 CORS 問題!你可以大方地直接呼叫雲端 HTTPS 網址。

2. 實體手機 Expo Go 的 Localhost 陷阱

如果今天沒有把後端推上 Cloud Run,而是讓 Spring Boot 跑在電腦本機 (localhost:8080)。請特別注意:當你用實體手機開啟 Expo Go 時,APP 裡的 localhost 指的是「手機自己」,而不是你的電腦! 如果要連到電腦,必須確保兩者在同一個 Wi-Fi 下,並將網址改為電腦的區域網路 IP(例如 http://192.168.1.100:8080)。不過我們已經把後端部署到 GCP Cloud Run 了!直接填上真實的 HTTPS 網址,可以避開區域網路與 IP 切換的大坑。

動手實作 1:封裝 API 服務層 (apiService.ts)

在終端機安裝 Axios:

npm install axios

與其在每個元件裡面散落著 axios.post(...),好的實務做法是建立一個獨立的 apiService.ts。因此我下了以下Prompt:

「我正在使用 Expo 開發 React Native APP。請幫我用 axios 封裝一個用來呼叫 Cloud Run AI 課表 > API 的非同步函式 generateMarathonPlan
需求:

  1. 設定 Base URL 支援環境變數注入(預設為我的 Cloud Run 網址)。
  2. 設定 30 秒的 Request Timeout(讓雲端 Gemini 有足夠時間運算)。
  3. 包含完善的 TypeScript 型別定義與 try-catch 錯誤處理。」

產出了的 API 封裝層 src/services/apiService.ts

import axios from 'axios';

// 隱藏實際網址,建議透過環境變數 EXPO_PUBLIC_API_URL 注入
export const BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'https://<YOUR-CLOUD-RUN-URL>';

const apiClient = axios.create({
  baseURL: BASE_URL,
  timeout: 30000,
  headers: {
    'Content-Type': 'application/json',
  },
});

export const generateMarathonPlan = async (payload) => {
  try {
    const response = await apiClient.post('/generate', payload);
    return response.data;
  } catch (error) {
    console.error('API 呼叫失敗:', error.response?.data || error.message);
    throw new Error(error.response?.data?.message || '無法喚醒 AI 教練,請檢查網路連線。');
  }
};

動手實作 2:AI 領域防護網(Guardrail)被觸發了?

滿懷期待地在實體手機 Expo Go 上發出請求後,手機畫面卻彈出了非預期的錯誤訊息:
這個錯誤訊息很眼熟——正是我們在 Day 11 後端設計的 AI Guardrail(領域防護網)!為什麼我們傳入賽事資料還會被擋下來?

把錯誤訊息交給 AI 進行跨端比對:

「我在手機 Expo Go 點選課表產生按鈕 時觸發了 DOMAIN_NOT_SUPPORTED 錯誤。 這是後端回傳的訊息:『教練我的眼裡只有東京馬拉松和跑鞋!問這什麼無關的問題,請出門左轉去找別人!』 請幫我查看是哪邊錯誤導致!」

排查結果:

比對了後端的 GenerateScheduleRequest.java 與 Prompt 範本:

  1. 前後端契約不一致:後端預期的欄位是 targetRace(目標賽事)與 currentLevel(跑者當前體能)
  2. 體能空白觸發防護網:前端原先只傳了 heightweight 等欄位,後端收不到 currentLevel,導致填入 Prompt 範本時「學員體能」呈現空白。Gemini 判定學員資訊不足以規劃安全訓練,因此直接回傳 isRelevant: false 拒絕回答!

找到原因後,立即將 Payload 組合成符合後端需求的格式:

const payload = {
  targetRace: "東京馬拉松 全程馬拉松",
  currentLevel: `跑者 ${profile.nickname},身高 ${profile.height} cm,體重 ${profile.weight} kg,目前每週可訓練 ${weeklyDays} 天,具備基礎體能與慢跑習慣。`
};

調整完後,再次透過 Expo Go 點擊發送——成功通過驗證!後端 Gemini 順利回傳了完整的 JSON 結構化課表!

動手實作 3:請 AI 重構 UX 流程(個人資料 vs. 課表生成分離)

在測試過程中,發現原本「每次儲存個人檔案都要重新呼叫一次 AI 課表」的體驗不太合理。因此 AI 進行架構重構**:**

「目前個人檔案頁面在點擊儲存時會直接發送 AI 請求,這不符合真實 App 的使用體驗。請幫我重構成兩階段流程:

  1. 個人檔案頁:專注管理跑者的暱稱、身高、體重與即時 BMI 計算,點擊儲存立即存入本地狀態(Zustand),不發送 AI 請求。
  2. 課表頁:提供賽事快速標籤(預設東京馬拉松系列避免打錯)與每週天數點選。點擊生成時才整合 Profile 數據呼叫 Cloud Run API,並加入 ActivityIndicator 讀取動畫與 disabled 防連點機制。」

AI 重構了兩個頁面,不僅讓職責更加分明,在 Expo Go 上的操作流暢度也大幅提升!完成後如下:

跑者基本檔案:
https://ithelp.ithome.com.tw/upload/images/20260826/20165043Oj4WaOsCcE.png

課表產生:
https://ithelp.ithome.com.tw/upload/images/20260826/201650437A1WO6wNTl.png

跑者視角的深度反思:AI 生成的課表合理嗎?

當看著實體手機 Expo Go 上成功渲染出 AI 產出的 4 週課表時,其實也發現到 AI 課表上的邏輯硬傷:

  1. 備賽週期過短:世界六大馬之一的東京馬拉松固定於每年 2 月底至 3 月初舉行。全馬備賽通常需要 12 ~ 16 週的週期化訓練(基礎適應、跑量堆疊、強度高峰、減量恢復),僅憑 4 週課表不可能讓跑者安全站上全馬起跑線。
  2. 跑量與強度不足:若目標是跑者嚮往的 Sub 4(破四小時),每週必須累積充足的長距離耐力(LSD)與配速門檻跑量,目前的生成跑量顯然太低。

這正是軟體開發與 AI 結合有趣的地方,並且也能夠考驗對產品的 Know How 程度!雖然我不是非常專業的跑步教練,但透過網路上的前輩分享,了解到具體的訓練計劃究竟是如何,才對此次 AI 的生成產生疑問 ——「串通 API 只是打通技術管道,如何透過專業領域知識調教出真正具備運動科學深度的 Prompt,才是產品的核心價值。」(這部分我們可到後續章節再來調教 AI 大腦!)

今日總結與明日預告

今天透過人機協作完成了幾項任務:

  • 掌握了 RN 原生環境無 CORS 限制與 Localhost 的避坑關鍵。
  • 請 AI 快速建立了強健的 Axios 服務層
  • 透過 Expo Go 實體手機成功連線 GCP Cloud Run 上的 Spring Boot 後端。
  • 透過 AI 跨端排查,解決了 AI Guardrail 的欄位契約問題。
  • 重構了個人檔案與課表生成流程,體驗更貼近真實產品。

目前我們已經在 Expo Go 成功拿到了這包珍貴的課表資料。明天(Day 18)將正式深入 Zustand 全域狀態管理與持久化儲存,教大家如何把這份雲端課表化為手機的全域記憶,讓各個分頁都能隨時存取!敬請期待!


上一篇
Day 16 | 介面實作 (1):打造跑者基本資料的專屬表單,為 AI 準備精準 Context
系列文
單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言