
后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载Redwood 应用时常需要消费非自有来源的数据本文以「输入美国邮编查询当前天气」为例完整演示在 Redwood 应用中接入第三方 APIOpenWeather的两种主流方案客户端React 应用直接调用以及服务端GraphQL 服务与 Service代理转发。读完本文你将掌握如何在 Redwood 的 Web 端使用redwoodjs/forms构建带校验的表单、在浏览器端用 Fetch API 直连第三方接口以及如何在 API 端通过 SDL Service Cell 的组合搭建一条安全、可复用的 GraphQL 数据通道并处理异常邮编等边界场景。场景设定一个天气查询应用我们构建一个极简的天气应用用户在首页输入美国邮编United States zip code应用调用 OpenWeather 的 Current Weather API把当前天气显示在页面上。为了简化示例代码假设只处理美国邮编。最终数据来自 OpenWeather 的天气接口该接口返回标准 JSON。应用只需要其中几个字段name邮编对应的城市名main.temp温度单位是开尔文 Kelvin展示时需要换算为华氏度或摄氏度weather[0].main英文天气描述如 Rain、Cloudsweather[0].icon天气图标编码可以拼成图片 URL 展示。本文给出客户端与服务端两种集成的完整代码两种实现都基于同一个前置准备步骤注册 OpenWeather 账号并获取 API Key。前置准备注册 OpenWeather 并获取 API Key在 OpenWeather 官网注册免费账号并验证邮箱。免费账号每天可调用 1,000 次足够示例应用使用。登录后在API keys页面复制默认 Key。注意新 Key 可能需要最多 30 分钟才会生效。在等待生效期间可以先用官方示例响应了解数据结构。调用示例zip 参数格式为邮编,国家码https://samples.openweathermap.org/data/2.5/weather?zip94040,usappid439d4b804bc8187953eb36d2a8c26a02返回的 JSON 示例{ coord: { lon: -122.09, lat: 37.39 }, weather: [ { id: 500, main: Rain, description: light rain, icon: 10d } ], base: stations, main: { temp: 280.44, pressure: 1017, humidity: 61, temp_min: 279.15, temp_max: 281.15 }, visibility: 12874, wind: { speed: 8.2, deg: 340, gust: 11.3 }, clouds: { all: 1 }, dt: 1519061700, sys: { type: 1, id: 392, message: 0.0027, country: US, sunrise: 1519051894, sunset: 1519091585 }, id: 0, name: Mountain View, cod: 200 }我们真正用到的字段集中在name、main.temp与weather数组上。天气图标可以直接通过http://openweathermap.org/img/wn/{icon}2x.png这样的 URL 加载icon取值如10d。创建 Redwood 应用与首页表单按照标准的 Redwood 启动流程创建项目并生成首页路由yarn create redwood-app weatherstation cd weatherstation yarn rw dev启动后浏览器会打开 http://localhost:8910。接着生成首页yarn rw generate page home /完整命令等价于yarn redwood generate page home /。生成的文件位于web/src/pages/HomePage/HomePage.js。打开该文件用 Redwood 表单组件构建邮编输入框import { Form, TextField, Submit } from redwoodjs/forms const HomePage () { const onSubmit (data) { console.info(data) } return ( Form onSubmit{onSubmit} style{{fontSize: 2rem}} TextField namezip placeholderZip code maxLength5 validation{{ required: true, pattern: /^\d{5}$/ }} / SubmitGo/Submit /Form ) } export default HomePage在浏览器中打开开发者工具点击Go控制台会输出表单数据对象包含zip字段。这里的关键是validation属性它基于 react-hook-form 的RegisterOptions语法required: true保证不能为空pattern: /^\d{5}$/保证恰好是 5 位数字。从源码看redwoodjs/forms的实现细节可以加深理解TextField并不是手写的独立组件而是由 InputComponents.tsx 中的元编程统一生成源码定义了一份INPUT_TYPES数组text、number、email、password 等 21 种类型随后通过循环把每种类型包装成对应的xxxField组件并导出TextField只是其中之一见 InputComponents.tsx。每个字段通过 useRegister.ts 中的useRegister钩子把name与validation注册进 react-hook-form 的register实现表单状态管理与校验。Form组件本身在 Form.tsx 中基于 react-hook-form 的useForm与handleSubmit实现onSubmit接收到的data就是校验通过后的表单值对象。至此表单层就绪。接下来要真正去请求天气数据有两条路线可选客户端直连浏览器中的 React 应用直接调用 OpenWeather API服务端代理Redwood 的 APIserverless function负责调用第三方 API客户端再通过 GraphQL 请求我们自己的服务。下文分别实现这两种集成。方式一客户端直接调用第三方 API先实现客户端直连版本。这种方式的核心优点与风险如下。优点设计最简单不需要搭建/设计服务端网络请求最少浏览器一次请求直达第三方速度快直连第三方 API。缺点不安全用户查看页面源码即可拿到 API Key无法限流攻击者可以写脚本每秒请求数千次。真实项目中需要自行权衡这些风险。在 onSubmit 中 Fetch 天气数据onSubmit已能拿到邮编直接在这里发起请求请替换为真实 API Keyconst onSubmit (data) { fetch(https://api.openweathermap.org/data/2.5/weather?zip66952,usappidYOUR_API_KEY) .then(response response.json()) .then(json console.info(json)) }注意如果 API Key 还没生效不能简单地改用官方示例响应地址——那会导致 CORS 错误。此时只能等待 Key 生效。然后替换掉硬编码的邮编使用模板字符串拼接用户在文本框里输入的data.zipconst onSubmit (data) { fetch(https://api.openweathermap.org/data/2.5/weather?zip${data.zip},usappidYOUR_API_KEY) .then(response response.json()) .then(json console.info(json)) }用 State 把结果渲染到页面数据已经拿到接下来用 React 的useState保存结果并驱动界面刷新。注意外层需要包一个 /fragmentimport { useState } from react import { Form, TextField, Submit } from redwoodjs/forms const HomePage () { const [weather, setWeather] useState() const onSubmit (data) { fetch( https://api.openweathermap.org/data/2.5/weather?zip${data.zip},usappidYOUR_API_KEY ) .then((response) response.json()) .then((json) setWeather(json)) } return ( Form onSubmit{onSubmit} TextField namezip placeholderZip code maxLength5 validation{{ required: true, pattern: /^\d{5}$/ }} / SubmitGo/Submit /Form {weather JSON.stringify(weather)} / ) } export default HomePage此时页面上会输出原始 JSON 文本。最后加上格式化辅助函数展示城市名、温度开尔文转华氏度和天气图标import { useState } from react import { Form, TextField, Submit } from redwoodjs/forms const HomePage () { const [weather, setWeather] useState() const onSubmit (data) { fetch( https://api.openweathermap.org/data/2.5/weather?zip${data.zip},usappidYOUR_API_KEY ) .then((response) response.json()) .then((json) setWeather(json)) } const temp () Math.round(((weather.main.temp - 273.15) * 9) / 5 32) const condition () weather.weather[0].main const icon () { return http://openweathermap.org/img/wn/${weather.weather[0].icon}2x.png } return ( Form onSubmit{onSubmit} TextField namezip placeholderZip code maxLength5 validation{{ required: true, pattern: /^\d{5}$/ }} / SubmitGo/Submit /Form {weather ( section h1{weather.name}/h1 h2 img src{icon()} style{{ maxWidth: 2rem }} / span {temp()}°F and {condition()} /span /h2 /section )} / ) } export default HomePage功能已经可用样式部分可自行美化。但请注意这个版本把 API Key 暴露在浏览器端安全性存在隐患。如果希望隐藏 Key、控制调用频率请看下面的服务端方案。方式二服务端 GraphQL 代理如果觉得客户端方案的风险不可接受就把第三方调用搬到服务端。为此需要做两件事为客户端提供一条与自家服务serverless function通信的途径自家服务再与第三方 API 通信。Redwood 内置 GraphQL 集成很自然地用 GraphQL 作为客户端与服务端的通信协议。实现上需要一份 GraphQL SDL定义客户端可见的接口和一个 Service真正实现调用第三方 API 的逻辑。Redwood 不是有 SDL 生成器吗Redwood 确实有 SDL generator但它假设你在api/db/schema.prisma中定义了模型生成的 SDL 是围绕该数据结构的 CRUD 接口。这里是自定义的第三方数据形状所以需要手写 SDL。定义 GraphQL APISDL既然数据结构可以自定义就趁机只保留客户端关心的字段并在服务端完成温度换算与图标 URL 拼接export const schema gql type Weather { zip: String! city: String! conditions: String! temp: Int! icon: String! } type Query { getWeather(zip: String!): Weather! skipAuth } 两点说明zip的类型是String而非Int因为美国邮编可能以0开头getWeather查询上标注了skipAuth表示该查询无需登录即可公开访问。在 Redwood 中skipAuth与requireAuth是内置的验证指令validator directive由redwoodjs/graphql-server提供自定义指令的创建机制见 makeDirectives.ts 中的createValidatorDirective。若希望该接口受登录保护把skipAuth换成requireAuth即可。编写 Service占位数据先行Redwood 中 GraphQL Query 类型会自动映射到同名 Service 导出函数所以创建weather.jsService 并导出getWeather。先用占位数据验证接口链路是否打通export const getWeather ({ zip }) { return { zip, city: City, conditions: Hot Lava, temp: 1000, icon: https://placekitten.com/100/100, } }这种「SDL 定义形状、Service 同名函数提供解析」的约定在 Redwood 中由 GraphQL 服务端的 schema 合并逻辑支撑相关测试可见 makeMergedSchema.test.ts。用 GraphQL Playground 验证Redwood 自带 GraphQL playground在浏览器打开 http://localhost:8911/graphql。把查询语句写到左上角变量zip写到左下角点击中间的 Play 按钮即可看到查询结果query GetWeatherQuery($zip: String!) { getWeather(zip: $zip) { zip city conditions temp icon } }变量区填写{ zip: 94040 }拉取真实数据接下来真正调用 OpenWeather。安装whatwg-node/fetch它在 Node 服务端模拟浏览器 Fetch APIyarn workspace api add whatwg-node/fetch因为fetch返回 Promise把 Service 改为async/await写法import { fetch } from whatwg-node/fetch export const getWeather async ({ zip }) { const response await fetch( http://api.openweathermap.org/data/2.5/weather?zip${zip},USappidYOUR_API_KEY ) const json await response.json() return { zip, city: json.name, conditions: json.weather[0].main, temp: Math.round(((json.main.temp - 273.15) * 9) / 5 32), icon: http://openweathermap.org/img/wn/${json.weather[0].icon}2x.png } }再次在 Playground 中点击 Play就能看到 OpenWeather 返回的真实数据。此时 API Key 只存在于服务端代码中客户端无法窥探。用 Cell 展示天气客户端通过 GraphQL 拿数据可以用 Redwood Cell 封装「发起请求、处理加载态、处理失败、渲染成功态」的全部样板代码。先生成 Cell 骨架yarn rw generate cell weather生成的文件web/src/components/WeatherCell/WeatherCell.js初始内容如下export const QUERY gql query FindWeatherQuery($id: Int!) { weather: weather(id: $id) { id } } export const Loading () divLoading.../div export const Empty () divEmpty/div export const Failure ({ error }) ( div style{{ color: red }}Error: {error.message}/div ) export const Success ({ weather }) { return div{JSON.stringify(weather)}/div }把QUERY改成匹配我们 API 签名export const QUERY gql query GetWeatherQuery($zip: String!) { weather: getWeather(zip: $zip) { zip city conditions temp icon } } 注意weather: getWeather这个别名它实际调用getWeather查询但响应字段会被重命名为weather并作为 prop 传给Success组件。从源码看Cell 的生命周期约定实现在 createCell.tsxLoading、Failure、Empty、Success分别对应不同的请求阶段beforeQuery默认把组件 props 当作 GraphQL 变量并采用cache-and-network的 fetch policy保证首次渲染即可看到缓存数据、同时后台刷新网络结果。先用HomePage接入WeatherCell并引入 state 记录用户提交的邮编import { Form, TextField, Submit } from redwoodjs/forms import { useState } from react import WeatherCell from src/components/WeatherCell const HomePage () { const [zip, setZip] useState() const onSubmit (data) { setZip(data.zip) } return ( Form onSubmit{onSubmit} style{{ fontSize: 2rem }} TextField namezip placeholderZip code maxLength5 validation{{ required: true, pattern: /^\d{5}$/ }} / SubmitGo/Submit /Form {zip WeatherCell zip{zip} /} / ) } export default HomePage此时页面上应显示 GraphQL 调用返回的 JSON。最后美化Success组件的输出export const Success ({ weather }) { return ( section h1{weather.city}/h1 h2 img src{weather.icon} style{{ maxWidth: 2rem }} / span {weather.temp}°F and {weather.conditions} /span /h2 /section ) }加分项处理无效邮编如果用户输入无效邮编如11111会发生什么Service 尝试从 OpenWeather 响应中取weather数组里的字段时会失败GraphQL 会抛出一个很粗糙的内部错误。先看看 OpenWeather 对不存在邮编的响应{ cod: 404, message: city not found }响应中的cod为404时说明邮编无效。在 Service 中增加检查抛出更友好的错误import { fetch } from whatwg-node/fetch import { UserInputError } from redwoodjs/graphql-server export const getWeather async ({ zip }) { const response await fetch( http://api.openweathermap.org/data/2.5/weather?zip${zip},USappidYOUR_API_KEY ) const json await response.json() if (json.cod 404) { throw new UserInputError(${zip} isnt a valid US zip code, please try again) } return { zip, city: json.name, conditions: json.weather[0].main, temp: Math.round(((json.main.temp - 273.15) * 9) / 5 32), icon: http://openweathermap.org/img/wn/${json.weather[0].icon}2x.png, } }UserInputError是redwoodjs/graphql-server提供的内置错误类型之一定义在 errors.ts。它继承自RedwoodGraphQLError会把错误码设置为BAD_USER_INPUT使 GraphQL 客户端能够识别这是「用户输入不合法」而非服务端故障同文件中还定义了AuthenticationErrorUNAUTHENTICATED、ForbiddenErrorFORBIDDEN等语义化错误类型。接下来去掉报错文案里生硬的 Error: 前缀并让它看起来更像错误提示。这交给 Cell 的Failure组件export const Failure ({ error }) ( span style{{ backgroundColor: #ffdfdf, color: #990000, padding: 0.5rem, display: inline-block, }} {error.message} /span )现在提交11111用户会看到清晰友好的错误提示而不是一团堆栈信息。两条路线的权衡与工程建议两种集成方式各有适用场景选择时可参考以下维度维度客户端直连服务端 GraphQL 代理实现复杂度低仅前端代码中SDL Service CellAPI Key 安全暴露在浏览器源码中只存在于服务端限流/防滥用无法控制可在 Service 内增加校验与限流网络请求数1 次浏览器 → 第三方2 次浏览器 → 自家 API → 第三方数据裁剪/格式化在前端处理可在服务端预处理客户端只拿所需字段鉴权接入无法与 Redwood 鉴权联动可与requireAuth等指令联动工程实践建议API Key 不要硬编码在代码里无论哪种方案都应通过 Redwood 的环境变量机制.env文件与process.env注入相关说明见 environment-variables.md。客户端方案中凡是会被浏览器下载的代码都会暴露密钥因此服务端方案才是隐藏密钥的正确姿势。对第三方响应做容错本文处理了cod 404的场景真实项目中还应处理网络超时、非 JSON 响应、第三方限流如 HTTP 429等异常。善用 Redwood 的既有设施表单校验来自 redwoodjs/formsCell 生命周期来自 redwoodjs/web 的 createCell服务端错误类型来自 redwoodjs/graphql-server它们都可以在 forms.md、cells.md、services.md、graphql.md、directives.md 等文档中找到更系统的用法。总结本文通过一个天气查询应用走完了 Redwood 接入第三方 API 的完整闭环客户端直连用redwoodjs/forms的表单拿到用户输入用浏览器 Fetch API 直连 OpenWeather用useState驱动界面更新——简单直接但 API Key 暴露且无法限流服务端 GraphQL 代理手写 SDL 定义精简的数据形状在 Service 中用whatwg-node/fetch拉取真实数据并完成温度换算用skipAuth/requireAuth控制访问权限用 Cell 封装查询生命周期最后用UserInputError优雅处理无效邮编。两种模式覆盖了 Redwood 应用中「消费外部数据」的绝大多数场景低频、非敏感的展示数据可以客户端直连涉及密钥、业务规则或需要复用给多个页面/端的数据则优先走 GraphQL 服务端代理。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐Redwood 集成第三方 API 实战从客户端直连到 GraphQL 服务端代理Redwood 集成第三方 API 实战从客户端直连到 GraphQL 服务端代理 本篇技术指南以 Redwood 框架为例完整演示如何在应用中接入你并不拥后端前端Web框架开发工具Redwood 应用接入第三方 API 完整实战从客户端直连到 GraphQL 服务端代理Redwood 应用接入第三方 API 完整实战从客户端直连到 GraphQL 服务端代理 本篇技术指南基于 Redwood 框架当前仓库 gh_mirro后端前端Web框架开发工具Redwood 应用接入第三方 API 的完整实战从客户端直连到服务端 GraphQL 网关Redwood 应用接入第三方 API 的完整实战从客户端直连到服务端 GraphQL 网关 在 Redwood 应用开发中数据并不总是来自自己的数据库——后端前端Web框架开发工具上一篇NVIDIA Profile Inspector3大突破性技巧解锁显卡隐藏性能的完全解决方案下一篇大麦抢票工具教程从环境到配置把抢票成功率榨出来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考