基于Django API的NextJS项目:最优CRUD架构模式选型
解决方案:跳过Next.js API直接对接GeoDjango的CRUD架构设计
一、首选方案:构建独立的CRUD服务层
直接封装自定义服务层是最清晰的方案,它职责单一(专注于和GeoDjango的交互),避免页面中零散的fetch调用,还能统一处理JWT令牌和请求异常。
实现步骤:
- 在项目根目录创建
services/api.js,封装基础请求逻辑和CRUD方法:
import { getSession } from 'next-auth/react'; // 基础请求封装:自动携带JWT、处理请求头和异常 const apiRequest = async (url, options = {}) => { const session = await getSession(); const baseHeaders = { 'Content-Type': 'application/json', ...(session?.user?.token && { Authorization: `Bearer ${session.user.token}` }) }; const response = await fetch(`https://你的GeoDjango域名${url}`, { ...options, headers: { ...baseHeaders, ...options.headers } }); if (!response.ok) { throw new Error(`请求失败: ${response.status} ${response.statusText}`); } return response.json(); }; // 封装GeoDjango的CRUD接口 export const geoAPI = { // 读取类接口(整合你已完成的逻辑) getLocations: () => apiRequest('/api/locations/'), getLocationById: (id) => apiRequest(`/api/locations/${id}/`), // 写入类接口 createLocation: (data) => apiRequest('/api/locations/', { method: 'POST', body: JSON.stringify(data) }), updateLocation: (id, data) => apiRequest(`/api/locations/${id}/`, { method: 'PUT', body: JSON.stringify(data) }), deleteLocation: (id) => apiRequest(`/api/locations/${id}/`, { method: 'DELETE' }) };
- 在页面/组件中直接调用服务层:
import { geoAPI } from '../services/api'; const CreateLocationPage = () => { const handleSubmit = async (formData) => { try { await geoAPI.createLocation(formData); // 处理成功逻辑:比如跳转列表页、提示用户 } catch (err) { // 统一处理错误:比如弹窗提示、日志上报 console.error('创建失败:', err); } }; return ( <form onSubmit={(e) => { e.preventDefault(); handleSubmit(new FormData(e.target)); }}> {/* 表单内容 */} </form> ); }; export default CreateLocationPage;
二、为什么不选Context或Middleware?
- Context:它的核心作用是全局状态共享(比如用户信息、主题配置),如果用来封装CRUD会让Context职责过重,且每次调用都要通过
useContext消费,不如直接导入服务层简洁。 - Middleware:Next.js的Middleware是处理路由级拦截(比如权限校验、重定向)的工具,完全不适合用来封装数据操作逻辑,属于误用场景。
三、Next.js替代Vue Store的方案
如果需要全局状态管理(比如缓存请求结果、同步数据状态),推荐两种方案:
- TanStack Query(原React Query):专门针对服务器数据的状态管理,自带缓存、自动刷新、失效机制,完美替代Vue Store中数据获取和状态维护的逻辑。结合服务层使用示例:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { geoAPI } from '../services/api'; const LocationsPage = () => { const queryClient = useQueryClient(); // 查询列表,自动缓存 const { data: locations } = useQuery({ queryKey: ['locations'], queryFn: geoAPI.getLocations }); // 创建数据的Mutation,成功后自动刷新列表缓存 const createMutation = useMutation({ mutationFn: geoAPI.createLocation, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['locations'] }); } }); return ( <> <button onClick={() => createMutation.mutate(你的表单数据)} disabled={createMutation.isLoading} > 添加位置 </button> {locations?.map(loc => ( <div key={loc.id}>{loc.name}</div> ))} </> ); };
- Zustand:轻量级全局状态库,如果需要维护非服务器数据的全局状态(比如表单草稿、UI开关状态),比Redux简单太多,适合中小型项目。
四、NextAuth JWT的最佳实践
- 客户端组件中用
getSession()获取令牌,服务层已经自动处理请求头携带逻辑,无需手动拼接。 - 服务器组件/
getServerSideProps中用getServerSession()获取令牌:
import { getServerSession } from 'next-auth/next'; import { authOptions } from '../pages/api/auth/[...nextauth]'; export const getServerSideProps = async () => { const session = await getServerSession(authOptions); const response = await fetch(`https://你的GeoDjango域名/api/locations/`, { headers: { ...(session?.user?.token && { Authorization: `Bearer ${session.user.token}` }) } }); const locations = await response.json(); return { props: { locations } }; };
- 务必在GeoDjango中配置CORS,允许你的Vercel域名发起跨域请求,否则会出现跨域报错。
推荐项目结构
你的Next.js项目/ ├── services/ │ └── api.js # CRUD服务层 ├── components/ │ └── LocationForm.js # 表单组件 ├── pages/ │ ├── locations.js # 列表页 │ └── create.js # 创建页 └── utils/ └── auth.js # 可选:封装NextAuth工具方法
内容的提问来源于stack exchange,提问作者Hvitis
相关产品推荐
相关产品推荐

