本教程詳細(xì)介紹了如何將react.js前端與php后端通過(guò)restful api進(jìn)行連接。文章將涵蓋后端api的構(gòu)建、前端數(shù)據(jù)請(qǐng)求與處理,以及跨域資源共享(cors)等關(guān)鍵配置,旨在幫助開(kāi)發(fā)者高效地構(gòu)建全棧web應(yīng)用。
在現(xiàn)代Web開(kāi)發(fā)中,前端與后端分離已成為主流實(shí)踐。React.js作為流行的前端庫(kù),負(fù)責(zé)構(gòu)建用戶界面;而PHP則常用于處理服務(wù)器端邏輯、數(shù)據(jù)庫(kù)交互和API服務(wù)。通過(guò)RESTful API,兩者可以無(wú)縫協(xié)作,共同構(gòu)建功能強(qiáng)大的Web應(yīng)用程序。
React.js應(yīng)用通常運(yùn)行在用戶的瀏覽器中,負(fù)責(zé)渲染UI和響應(yīng)用戶操作。它通過(guò)HTTP請(qǐng)求(如GET、POST、PUT、DELETE)與后端API進(jìn)行通信,獲取或提交數(shù)據(jù)。PHP后端則接收這些請(qǐng)求,處理業(yè)務(wù)邏輯,與數(shù)據(jù)庫(kù)交互,并以JSON等格式返回?cái)?shù)據(jù)給前端。這種模式的核心是RESTful API,它定義了一套標(biāo)準(zhǔn)化的接口,使得不同技術(shù)棧的組件能夠互相理解和通信。
為了將原始的PHP CLI腳本轉(zhuǎn)換為可供React.js調(diào)用的Web API,我們需要進(jìn)行以下改造:
假設(shè)我們有一個(gè)data.json文件作為數(shù)據(jù)源:
立即學(xué)習(xí)“PHP免費(fèi)學(xué)習(xí)筆記(深入)”;
[ { "offerId": 1, "productTitle": "Laptop", "vendorId": 101, "price": 1200 }, { "offerId": 2, "productTitle": "Mouse", "vendorId": 101, "price": 25 }, { "offerId": 3, "productTitle": "Keyboard", "vendorId": 102, "price": 75 }, { "offerId": 4, "productTitle": "Monitor", "vendorId": 103, "price": 300 }, { "offerId": 5, "productTitle": "Webcam", "vendorId": 102, "price": 50 }, { "offerId": 6, "productTitle": "Headphones", "vendorId": 101, "price": 150 } ]
我們將原有的PHP代碼封裝為一個(gè)API入口文件 api.php:
<?php // 設(shè)置CORS頭,允許React開(kāi)發(fā)服務(wù)器訪問(wèn) header("Access-Control-Allow-Origin: http://localhost:3000"); // 替換為你的React應(yīng)用地址 header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // 處理OPTIONS請(qǐng)求,用于CORS預(yù)檢 if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit(); } /** * The interface provides the contract for different readers * E.g. it can be XML/JSON Remote Endpoint, or CSV/JSON/XML local files */ interface ReaderInterface { /** * Read in incoming data and parse to objects */ public function read(string $input): OfferCollectionInterface; } /** * Interface of Data Transfer Object, that represents external JSON data */ interface OfferInterface { } /** * Interface for The Collection class that contains Offers */ interface OfferCollectionInterface { public function get(int $index): OfferInterface; public function getIterator(): Iterator; } /* *********************************** */ class Offer implements OfferInterface { public $offerId; public $productTitle; public $vendorId; public $price; public function __toString(): string { return "$this->offerId | $this->productTitle | $this->vendorId | $this->price\n"; } } class OfferCollection implements OfferCollectionInterface { private $offersList = array(); public function __construct($data) { if (is_array($data)) { foreach ($data as $json_object) { $offer = new Offer(); $offer->offerId = $json_object->offerId; $offer->productTitle = $json_object->productTitle; $offer->vendorId = $json_object->vendorId; $offer->price = $json_object->price; array_push($this->offersList, $offer); } } } public function get(int $index): OfferInterface { return $this->offersList[$index]; } public function getIterator(): Iterator { return new ArrayIterator($this->offersList); } public function __toString(): string { return implode("\n", $this->offersList); } // 新增方法:將OfferCollection轉(zhuǎn)換為數(shù)組,以便json_encode public function toArray(): array { $result = []; foreach ($this->offersList as $offer) { $result[] = [ 'offerId' => $offer->offerId, 'productTitle' => $offer->productTitle, 'vendorId' => $offer->vendorId, 'price' => $offer->price, ]; } return $result; } } class Reader implements ReaderInterface { /** * Read in incoming data and parse to objects */ public function read(string $input): OfferCollectionInterface { if ($input != null) { $content = file_get_contents($input); $json = json_decode($content); $result = new OfferCollection($json); return $result; } return new OfferCollection(null); } } class Logger { private $filename = "logs.txt"; public function info($message): void { $this->log($message, "INFO"); } public function error($message): void { $this->log($message, "ERROR"); } private function log($message, $type): void { $myfile = fopen($this->filename, "a") or die("Unable to open file!"); $txt = "[$type] $message\n"; fwrite($myfile, $txt); fclose($myfile); } } $json_url = 'data.json'; $json_reader = new Reader(); $offers_list = $json_reader->read($json_url); function count_by_price_range($price_from, $price_to) { global $offers_list; $count = 0; foreach ($offers_list->getIterator() as $offer) { if ($offer->price >= $price_from && $offer->price <= $price_to) { $count++; } } return $count; } function count_by_vendor_id($vendorId) { global $offers_list; $count = 0; foreach ($offers_list->getIterator() as $offer) { if ($offer->vendorId == $vendorId) { $count++; } } return $count; } // 獲取請(qǐng)求路徑和參數(shù) $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $path_segments = explode('/', trim($request_uri, '/')); $api_endpoint = end($path_segments); // 假設(shè)API路徑的最后一段是功能名稱 $logger = new Logger(); $response_data = []; $status_code = 200; switch ($api_endpoint) { case "count_by_price_range": { $price_from = $_GET['from'] ?? null; $price_to = $_GET['to'] ?? null; if ($price_from !== null && $price_to !== null) { $logger->info("Getting Count By Price Range From: $price_from TO $price_to"); $response_data = ['count' => count_by_price_range((float)$price_from, (float)$price_to)]; } else { $status_code = 400; $response_data = ['error' => 'Missing price range parameters (from, to).']; } break; } case "count_by_vendor_id": { $vendorId = $_GET['vendorId'] ?? null; if ($vendorId !== null) { $logger->info("Getting Count By vendor Id: $vendorId"); $response_data = ['count' => count_by_vendor_id((int)$vendorId)]; } else { $status_code = 400; $response_data = ['error' => 'Missing vendorId parameter.']; } break; } case "offers": { // 新增一個(gè)獲取所有offer的接口 $response_data = ['offers' => $offers_list->toArray()]; break; } default: { $status_code = 404; $response_data = ['error' => 'API endpoint not found.']; break; } } http_response_code($status_code); echo json_encode($response_data); ?>
將 api.php 和 data.json 放在一個(gè)支持PHP的Web服務(wù)器(如Apache或Nginx)的根目錄下。 現(xiàn)在,你可以通過(guò)訪問(wèn)類似 http://localhost/api.php/count_by_price_range?from=50&to=200 或 http://localhost/api.php/offers 來(lái)測(cè)試API。
接下來(lái),我們創(chuàng)建一個(gè)簡(jiǎn)單的React組件來(lái)調(diào)用這個(gè)PHP API并顯示數(shù)據(jù)。
初始化React項(xiàng)目 如果你還沒(méi)有React項(xiàng)目,可以使用Create React App快速搭建:
npx create-react-app react-php-app cd react-php-app npm start
編寫(xiě)React組件 修改 src/App.js 文件,添加一個(gè)組件來(lái)獲取并展示數(shù)據(jù):
import React, { useState, useEffect } from 'react'; import './App.css'; function App() { const [offers, setOffers] = useState([]); const [priceRangeCount, setPriceRangeCount] = useState(0); const [vendorIdCount, setVendorIdCount] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // PHP API 的基礎(chǔ)URL,請(qǐng)根據(jù)你的實(shí)際部署修改 const API_BASE_URL = 'http://localhost/api.php'; useEffect(() => { const fetchData = async () => { try { // 獲取所有Offers const offersResponse = await fetch(`${API_BASE_URL}/offers`); if (!offersResponse.ok) { throw new Error(`HTTP error! status: ${offersResponse.status}`); } const offersData = await offersResponse.json(); setOffers(offersData.offers || []); // 獲取價(jià)格區(qū)間統(tǒng)計(jì) const priceRangeResponse = await fetch(`${API_BASE_URL}/count_by_price_range?from=50&to=200`); if (!priceRangeResponse.ok) { throw new Error(`HTTP error! status: ${priceRangeResponse.status}`); } const priceRangeData = await priceRangeResponse.json(); setPriceRangeCount(priceRangeData.count || 0); // 獲取供應(yīng)商ID統(tǒng)計(jì) const vendorIdResponse = await fetch(`${API_BASE_URL}/count_by_vendor_id?vendorId=101`); if (!vendorIdResponse.ok) { throw new Error(`HTTP error! status: ${vendorIdResponse.status}`); } const vendorIdData = await vendorIdResponse.json(); setVendorIdCount(vendorIdData.count || 0); } catch (error) { console.error("Error fetching data:", error); setError(error); } finally { setLoading(false); } }; fetchData(); }, []); // 空數(shù)組表示只在組件掛載時(shí)運(yùn)行一次 if (loading) return <div>Loading data...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div className="App"> <h1>React.js & PHP API 集成示例</h1> <h2>所有商品列表</h2> {offers.length > 0 ? ( <ul> {offers.map(offer => ( <li key={offer.offerId}> ID: {offer.offerId}, Title: {offer.productTitle}, Vendor: {offer.vendorId}, Price: ${offer.price} </li> ))} </ul> ) : ( <p>沒(méi)有商品數(shù)據(jù)。</p> )} <h2>統(tǒng)計(jì)信息</h2> <p>價(jià)格在 $50 到 $200 之間的商品數(shù)量: {priceRangeCount}</p> <p>供應(yīng)商ID為 101 的商品數(shù)量: {vendorIdCount}</p> </div> ); } export default App;
在開(kāi)發(fā)階段,React應(yīng)用通常運(yùn)行在 http://localhost:3000,而PHP后端可能運(yùn)行在 http://localhost 或 http://localhost:80。由于它們端口或域名不同,瀏覽器會(huì)阻止React應(yīng)用直接訪問(wèn)PHP API,這就是所謂的“跨域”問(wèn)題。
為了解決這個(gè)問(wèn)題,PHP后端需要發(fā)送特定的HTTP響應(yīng)頭,告知瀏覽器允許來(lái)自React應(yīng)用源的請(qǐng)求。在 api.php 的開(kāi)頭,我們已經(jīng)添加了以下CORS頭:
header("Access-Control-Allow-Origin: http://localhost:3000"); // 允許來(lái)自React開(kāi)發(fā)服務(wù)器的請(qǐng)求 header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); // 允許的HTTP方法 header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // 處理OPTIONS請(qǐng)求,用于CORS預(yù)檢 if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit(); }
通過(guò)RESTful API,React.js和PHP可以高效地協(xié)同工作,分別專注于前端的用戶體驗(yàn)和后端的業(yè)務(wù)邏輯。構(gòu)建一個(gè)穩(wěn)健的API涉及請(qǐng)求處理、數(shù)據(jù)格式化、CORS配置以及安全性考量。遵循本教程的指導(dǎo),開(kāi)發(fā)者可以順利地將React.js前端與PHP后端集成,構(gòu)建出功能完善、結(jié)構(gòu)清晰的Web應(yīng)用程序。
以上就是React.js與PHP后端集成:構(gòu)建RESTful API應(yīng)用教程的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
PHP怎么學(xué)習(xí)?PHP怎么入門(mén)?PHP在哪學(xué)?PHP怎么學(xué)才快?不用擔(dān)心,這里為大家提供了PHP速學(xué)教程(入門(mén)到精通),有需要的小伙伴保存下載就能學(xué)習(xí)啦!
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)