¥
立即购买

API端点技术文档生成器助手

20 浏览
1 试用
0 购买
Dec 5, 2025更新

本提示词专为iOS开发场景设计,能够根据API端点的功能特性,生成结构清晰、内容完整的技术文档。通过系统化的分析流程,确保文档包含端点概述、请求参数、响应格式、错误代码和使用示例等核心要素,帮助开发团队快速理解和使用API接口,提升开发效率和协作质量。生成的文档风格专业严谨,逻辑层次分明,便于后续维护和参考。

接口概述

用于移动端登录场景下获取 OAuth 2.0 访问令牌的端点。支持两种授权类型:

  • 密码模式(password):使用邮箱和密码登录,首次获取令牌。
  • 刷新令牌模式(refresh_token):使用已有的刷新令牌获取新的访问令牌。

该端点不需要现有令牌即可调用。适用于用户登录、令牌续期等场景。速率限制:20次/分钟。

请求方式

  • HTTP 方法:POST
  • URL 路径:/api/v1/oauth/token
  • 请求头:
    • Content-Type: application/json
  • 鉴权:无需

请求参数

说明:请求体为 JSON。根据 grant_type 不同,所需参数不同。

通用参数

  • grant_type
    • 类型:string
    • 必填:是
    • 允许值:password 或 refresh_token
    • 描述:授权类型

按授权类型的参数

当 grant_type = password:

  • username
    • 类型:string
    • 必填:是
    • 约束:邮箱格式
    • 描述:用户邮箱
  • password
    • 类型:string
    • 必填:是
    • 约束:长度≥8
    • 描述:用户密码
  • scope
    • 类型:string
    • 必填:否
    • 允许值:空字符串或 basic
    • 描述:权限范围(可为空)

当 grant_type = refresh_token:

  • refresh_token
    • 类型:string
    • 必填:是
    • 描述:刷新令牌
  • scope
    • 类型:string
    • 必填:否
    • 允许值:空字符串或 basic
    • 描述:权限范围(可为空)

响应格式

成功时返回 JSON,字段如下:

  • access_token
    • 类型:string
    • 描述:Bearer 访问令牌
  • token_type
    • 类型:string
    • 描述:令牌类型(值为 Bearer)
  • expires_in
    • 类型:int(秒)
    • 描述:access_token 的有效期(秒)
  • refresh_token
    • 类型:string
    • 描述:用于刷新访问令牌的刷新令牌

成功响应示例: { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "def50200a1b2c3d4..." }

错误代码

  • 400 invalid_grant
    • 含义:授权失败(如账号或密码错误,或刷新令牌无效/过期)
  • 429 rate_limited
    • 含义:超出速率限制(20次/分钟)

注:错误响应的具体报文结构未在本规范中定义,请根据状态码与错误码进行处理。

使用示例

请求示例(密码模式)

  • 请求
    • POST /api/v1/oauth/token
    • 头:Content-Type: application/json
    • 体: { "grant_type": "password", "username": "user@example.com", "password": "P@ssw0rd123", "scope": "basic" }

请求示例(刷新令牌模式)

  • 请求
    • POST /api/v1/oauth/token
    • 头:Content-Type: application/json
    • 体: { "grant_type": "refresh_token", "refresh_token": "def50200a1b2c3d4...", "scope": "" }

cURL 示例(密码模式) curl -X POST "https:///api/v1/oauth/token"
-H "Content-Type: application/json"
-d '{ "grant_type": "password", "username": "user@example.com", "password": "P@ssw0rd123", "scope": "basic" }'

cURL 示例(刷新令牌模式) curl -X POST "https:///api/v1/oauth/token"
-H "Content-Type: application/json"
-d '{ "grant_type": "refresh_token", "refresh_token": "def50200a1b2c3d4...", "scope": "" }'

iOS(Swift)示例:使用 URLSession 获取令牌并写入 Keychain import Foundation import Security

struct TokenResponse: Decodable { let access_token: String let token_type: String let expires_in: Int let refresh_token: String }

struct PasswordGrantBody: Encodable { let grant_type = "password" let username: String let password: String let scope: String? }

struct RefreshGrantBody: Encodable { let grant_type = "refresh_token" let refresh_token: String let scope: String? }

final class OAuthTokenService { private let baseURL = URL(string: "https://")! private lazy var session: URLSession = { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 15 // 15s 超时 return URLSession(configuration: config) }()

func requestPasswordToken(username: String,
                          password: String,
                          scope: String? = nil,
                          completion: @escaping (Result<TokenResponse, Error>) -> Void) {
    let body = PasswordGrantBody(username: username, password: password, scope: scope)
    performTokenRequest(body: body, completion: completion)
}

func requestRefreshToken(refreshToken: String,
                         scope: String? = nil,
                         completion: @escaping (Result<TokenResponse, Error>) -> Void) {
    let body = RefreshGrantBody(refresh_token: refreshToken, scope: scope)
    performTokenRequest(body: body, completion: completion)
}

private func performTokenRequest<T: Encodable>(body: T,
                                               completion: @escaping (Result<TokenResponse, Error>) -> Void) {
    var url = baseURL
    url.appendPathComponent("/api/v1/oauth/token")

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    do {
        request.httpBody = try JSONEncoder().encode(body)
    } catch {
        completion(.failure(error))
        return
    }

    session.dataTask(with: request) { data, response, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        guard let http = response as? HTTPURLResponse, let data = data else {
            completion(.failure(NSError(domain: "net", code: -1)))
            return
        }

        guard (200...299).contains(http.statusCode) else {
            // 建议根据 http.statusCode(如 400 / 429)做分类处理
            completion(.failure(NSError(domain: "http", code: http.statusCode)))
            return
        }

        do {
            let token = try JSONDecoder().decode(TokenResponse.self, from: data)
            completion(.success(token))
        } catch {
            completion(.failure(error))
        }
    }.resume()
}

}

// Keychain 存储示例(简化版) @discardableResult func storeTokenInKeychain(accessToken: String, refreshToken: String) -> Bool { func upsert(account: String, value: String) -> Bool { let service = "com.yourapp.oauth" let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] let attrs: [String: Any] = [ kSecValueData as String: Data(value.utf8), kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock ] let status = SecItemUpdate(query as CFDictionary, attrs as CFDictionary) if status == errSecItemNotFound { var add = query add.merge(attrs) { $1 } return SecItemAdd(add as CFDictionary, nil) == errSecSuccess } return status == errSecSuccess }

let ok1 = upsert(account: "access_token", value: accessToken)
let ok2 = upsert(account: "refresh_token", value: refreshToken)
return ok1 && ok2

}

// 使用示例 let service = OAuthTokenService() service.requestPasswordToken(username: "user@example.com", password: "P@ssw0rd123", scope: "basic") { result in switch result { case .success(let token): _ = storeTokenInKeychain(accessToken: token.access_token, refreshToken: token.refresh_token) case .failure(let error): print("Token request failed:", error) } }

注意事项

  • 协议与安全
    • 强制使用 HTTPS 访问该端点。
    • 令牌请存储于 iOS Keychain,避免使用 UserDefaults/明文文件;避免在日志中输出令牌。
  • 超时与重试
    • iOS 客户端建议将请求超时设置为 15 秒。
    • 收到 429(rate_limited)时,应停止立即重试,等待一段时间后再发起请求,避免超过 20次/分钟 的限制。
  • 授权与参数
    • grant_type 仅支持 password 与 refresh_token。
    • username 需为邮箱格式,password 长度≥8。
    • scope 可为空字符串或 basic。
  • 令牌管理
    • token_type 为 Bearer,发起后续需要鉴权的 API 请求时,应使用标准 Authorization: Bearer <access_token> 头部(仅在需要鉴权的端点使用)。
    • 在 access_token 即将过期前可使用 refresh_token 获取新令牌;refresh_token 需妥善保存。
  • 其他
    • 基础域名()请按环境配置(开发/测试/生产)。

接口概述

为已登录用户提供订单列表的只读分页检索能力。客户端可通过可选的状态过滤、更新时间过滤及分页参数获取订单数据。典型使用场景:

  • 订单中心列表页的首次加载与下拉刷新
  • 上拉加载更多分页数据
  • 按状态筛选(待支付、已支付、已发货、已取消)
  • 基于更新时间的增量同步(配合 ETag 与 updated_after)

支持 iOS 端通过 Authorization 认证访问,并支持 ETag 条件请求以减少流量和加快加载。

请求方式

  • HTTP 方法:GET
  • 路径:/api/v1/orders
  • 认证:Authorization: Bearer (必需)

请求头(建议/可选):

  • Accept: application/json
  • If-None-Match: (可选,用于条件请求)

请求参数

说明:

  • 所有查询参数均可选,若未提供则由服务端采用默认策略(本文档未定义)。
  • 参数校验不通过时,服务端可能返回 422。

查询参数:

  • status
    • 类型:string
    • 是否必填:否
    • 取值枚举:pending, paid, shipped, canceled
    • 说明:按订单状态筛选。
  • page
    • 类型:int
    • 是否必填:否
    • 范围:>= 1
    • 说明:分页页码。用于获取指定页数据。
  • page_size
    • 类型:int
    • 是否必填:否
    • 范围:<= 100
    • 说明:每页返回的记录数上限。
  • updated_after
    • 类型:string
    • 是否必填:否
    • 格式:RFC3339(例如:2024-01-10T12:00:00Z)
    • 说明:仅返回该时间点之后更新的订单,用于增量同步。

响应格式

成功响应(HTTP 200)为 JSON 对象,包含以下字段:

  • items:array[object]
    • order_id:string
    • status:string(枚举:pending, paid, shipped, canceled)
    • amount:number(建议在客户端使用 Decimal 处理货币)
    • currency:string(ISO-4217 货币代码,如 "USD", "CNY")
    • created_at:string(ISO8601 日期时间,例如:2024-01-10T12:00:00Z)
  • meta:object
    • total:int(符合当前筛选条件的总记录数)
    • page:int(当前页码)
    • page_size:int(当前页大小)
    • has_next:bool(是否存在下一页)

条件请求(ETag):

  • 响应可能包含响应头:ETag: ""
  • 当客户端携带 If-None-Match: "" 且数据未变化时,服务端可返回 304 Not Modified(无响应体)。此状态不属于错误。

错误代码

  • 401 Unauthorized:未提供或提供了无效的令牌(Authorization 失败)。
  • 403 Forbidden:已认证,但无访问该资源的权限。
  • 422 Unprocessable Entity:参数不合法(例如:page < 1、page_size > 100、status 非法、updated_after 格式不符合 RFC3339)。
  • 500 Internal Server Error:服务器内部错误。

使用示例

示例请求(带筛选与分页): GET /api/v1/orders?status=paid&page=1&page_size=20&updated_after=2024-01-10T12:00:00Z Accept: application/json Authorization: Bearer If-None-Match: "W/"e19d5cd5af0378da05f63f891c7467af""

示例成功响应(200 OK): { "items": [ { "order_id": "ord_202501011234", "status": "paid", "amount": 199.99, "currency": "USD", "created_at": "2025-01-02T08:30:15Z" }, { "order_id": "ord_202501011235", "status": "paid", "amount": 59.50, "currency": "USD", "created_at": "2025-01-02T10:12:00Z" } ], "meta": { "total": 142, "page": 1, "page_size": 20, "has_next": true } }

示例条件请求未修改(304 Not Modified,无响应体): HTTP/1.1 304 Not Modified

iOS 端 Swift 代码示例(URLComponents 构建查询 + ISO8601 解析 + ETag 缓存):

// 1) 定义模型 struct OrdersResponse: Decodable { let items: [Order] let meta: Meta

struct Order: Decodable {
    let orderId: String
    let status: String
    let amount: Decimal
    let currency: String
    let createdAt: Date

    enum CodingKeys: String, CodingKey {
        case orderId = "order_id"
        case status
        case amount
        case currency
        case createdAt = "created_at"
    }
}

struct Meta: Decodable {
    let total: Int
    let page: Int
    let pageSize: Int
    let hasNext: Bool

    enum CodingKeys: String, CodingKey {
        case total, page
        case pageSize = "page_size"
        case hasNext = "has_next"
    }
}

}

// 2) 灵活的 ISO8601 解码(兼容是否带毫秒) final class ISO8601FlexibleDecoder { static let withoutFractional: ISO8601DateFormatter = { let f = ISO8601DateFormatter() f.formatOptions = [.withInternetDateTime, .withColonSeparatorInTimeZone] return f }()

static let withFractional: ISO8601DateFormatter = {
    let f = ISO8601DateFormatter()
    f.formatOptions = [.withInternetDateTime, .withFractionalSeconds, .withColonSeparatorInTimeZone]
    return f
}()

static func decoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .useDefaultKeys
    decoder.dateDecodingStrategy = .custom { decoder -> Date in
        let container = try decoder.singleValueContainer()
        let str = try container.decode(String.self)
        if let d = withFractional.date(from: str) ?? withoutFractional.date(from: str) {
            return d
        }
        throw DecodingError.dataCorruptedError(in: container,
                                               debugDescription: "Invalid ISO8601 date: \(str)")
    }
    return decoder
}

}

// 3) 简易 ETag 缓存(内存演示,可替换为持久化方案) final class ETagCache { static let shared = ETagCache() private var store: [String: (etag: String, data: Data)] = [:] private let lock = NSLock()

func key(for url: URL) -> String { url.absoluteString }

func get(for url: URL) -> (etag: String, data: Data)? {
    lock.lock(); defer { lock.unlock() }
    return store[key(for: url)]
}

func set(for url: URL, etag: String, data: Data) {
    lock.lock(); defer { lock.unlock() }
    store[key(for: url)] = (etag, data)
}

}

// 4) 构建请求并发起调用 struct OrdersQuery { var status: String? // "pending"/"paid"/"shipped"/"canceled" var page: Int? // >= 1 var pageSize: Int? // <= 100 var updatedAfter: Date? // RFC3339/ISO8601 }

func fetchOrders(baseURL: URL, token: String, query: OrdersQuery, completion: @escaping (Result<OrdersResponse, Error>) -> Void) { // URLComponents 构建查询参数 var components = URLComponents(url: baseURL.appendingPathComponent("/api/v1/orders"), resolvingAgainstBaseURL: false)! var items: [URLQueryItem] = []

if let status = query.status { items.append(URLQueryItem(name: "status", value: status)) }
if let page = query.page { items.append(URLQueryItem(name: "page", value: String(page))) }
if let pageSize = query.pageSize {
    let clamped = min(pageSize, 100)  // 客户端侧预防性限制
    items.append(URLQueryItem(name: "page_size", value: String(clamped)))
}
if let updatedAfter = query.updatedAfter {
    let fmt = ISO8601FlexibleDecoder.withFractional // 输出 RFC3339/ISO8601 字符串
    let str = fmt.string(from: updatedAfter)
    items.append(URLQueryItem(name: "updated_after", value: str))
}
components.queryItems = items

guard let url = components.url else {
    completion(.failure(NSError(domain: "OrdersAPI", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
    return
}

var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

// 附加 If-None-Match(若有)
if let cached = ETagCache.shared.get(for: url) {
    request.setValue(cached.etag, forHTTPHeaderField: "If-None-Match")
}

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let error = error {
        completion(.failure(error)); return
    }
    guard let http = response as? HTTPURLResponse else {
        completion(.failure(NSError(domain: "OrdersAPI", code: -2, userInfo: [NSLocalizedDescriptionKey: "No HTTP response"])))
        return
    }

    // 304:返回缓存数据
    if http.statusCode == 304 {
        if let cached = ETagCache.shared.get(for: url) {
            let decoder = ISO8601FlexibleDecoder.decoder()
            do {
                let result = try decoder.decode(OrdersResponse.self, from: cached.data)
                completion(.success(result))
            } catch {
                completion(.failure(error))
            }
        } else {
            completion(.failure(NSError(domain: "OrdersAPI", code: 304, userInfo: [NSLocalizedDescriptionKey: "Not Modified but no cache"])))
        }
        return
    }

    // 非 200:转化为错误(实际项目可按 401/403/422/500 分流处理)
    guard http.statusCode == 200, let data = data else {
        completion(.failure(NSError(domain: "OrdersAPI", code: http.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP \(http.statusCode)"])))
        return
    }

    // 解析并存储 ETag
    if let etag = http.value(forHTTPHeaderField: "ETag") {
        ETagCache.shared.set(for: url, etag: etag, data: data)
    }

    let decoder = ISO8601FlexibleDecoder.decoder()
    do {
        let result = try decoder.decode(OrdersResponse.self, from: data)
        completion(.success(result))
    } catch {
        completion(.failure(error))
    }
}
task.resume()

}

// 5) 加载下一页示例 func loadNextPageIfNeeded(current: OrdersResponse, baseURL: URL, token: String, status: String?) { guard current.meta.hasNext else { return } let nextPage = current.meta.page + 1 let query = OrdersQuery(status: status, page: nextPage, pageSize: current.meta.pageSize, updatedAfter: nil) fetchOrders(baseURL: baseURL, token: token, query: query) { result in // 合并列表 UI... } }

注意事项

  • 认证
    • 必须在请求头中携带 Authorization: Bearer 。令牌无效或缺失将导致 401。
  • 参数有效性
    • page 必须为 >= 1;page_size 建议不超过 100(超过可能返回 422)。
    • status 必须在指定枚举中;updated_after 必须符合 RFC3339。
  • 时间与解析
    • 响应字段 created_at 为 ISO8601 字符串;建议使用 ISO8601DateFormatter,并兼容是否包含毫秒。
    • updated_after 建议使用 UTC(以 Z 结尾)提交。
  • 货币与金额
    • amount 为数值;建议在客户端使用 Decimal 进行金额计算/显示,避免浮点精度误差。
    • currency 为 ISO-4217 代码(长度 3,例如 USD、CNY)。
  • 分页
    • 使用 meta.page、meta.page_size、meta.has_next 控制翻页逻辑;当 has_next 为 false 时停止继续请求。
  • 缓存与 ETag
    • 优先使用服务器下发的 ETag 与 If-None-Match 发起条件请求;当返回 304 时使用本地缓存数据,降低流量与延迟。
    • 建议将 ETag 与响应体按完整 URL(含查询串)作为键进行缓存。
  • 错误处理
    • 401:引导用户重新登录或刷新令牌。
    • 403:提示无权限操作。
    • 422:提示参数不合法并回退到安全值(例如将 page 重置为 1,将 page_size 限制为 100)。
    • 500:提示服务器错误并允许用户重试。
  • 连接与安全
    • 建议仅通过 HTTPS 访问并校验证书;设置合理的超时重试策略。
  • 兼容性
    • 若需要兼容不同的服务器时间格式,建议使用灵活的 ISO8601 解析策略(如示例所示),以提升鲁棒性。

接口概述

用于上传并更新用户头像的端点。服务端会对上传图片进行正方形自动裁剪,返回裁剪后图片的元数据与访问地址。支持 Idempotency-Key 以避免重复创建资源。适用于个人中心头像设置/更新等场景。

请求方式

  • HTTP 方法:POST
  • URL 路径:/api/v1/media/avatar
  • 认证方式:Authorization: Bearer
  • 内容类型:multipart/form-data

请求参数

  • 请求头

    • Authorization (string, 必填):Bearer
    • Idempotency-Key (string, 可选):用于保证请求幂等性(相同请求与键不重复创建资源)
  • 表单字段(multipart/form-data)

    • file (binary, 必填):JPEG/PNG 图片,大小 ≤ 5MB
    • filename (string, 可选):原始文件名(用于服务端记录/日志)
    • purpose (string, 可选):固定值 "avatar"

响应格式

  • 成功(200/201)
    • Content-Type: application/json
    • 字段说明:
      • media_id (string):媒体资源唯一标识
      • file_url (string):头像文件的 HTTPS 访问地址
      • width (int):服务端处理(裁剪)后的像素宽度
      • height (int):服务端处理(裁剪)后的像素高度
      • content_type (string):文件 MIME 类型(image/jpeg 或 image/png)

示例: { "media_id": "avt_1234567890", "file_url": "https://cdn.example.com/media/avt_1234567890.jpg", "width": 512, "height": 512, "content_type": "image/jpeg" }

错误代码

  • 400 Bad Request:请求参数不合法(缺少 file、purpose 非法等)
  • 401 Unauthorized:未提供或 token 无效
  • 413 Payload Too Large:文件超过 5MB 限制
  • 415 Unsupported Media Type:不支持的文件类型(仅支持 JPEG/PNG)

使用示例

  • cURL curl -X POST https://api.example.com/api/v1/media/avatar
    -H "Authorization: Bearer "
    -H "Idempotency-Key: 8f0f9a0a-6e7b-4c77-9c1c-2a2d7c0f0abc"
    -F "file=@/path/to/avatar.jpg"
    -F "filename=avatar.jpg"
    -F "purpose=avatar"

  • iOS(Swift,URLSession 背景上传,超时 60s,上传前压缩至 <2MB 优化) import UIKit

    final class AvatarUploader: NSObject, URLSessionTaskDelegate { private let boundary = "Boundary-(UUID().uuidString)" private lazy var session: URLSession = { let config = URLSessionConfiguration.background(withIdentifier: "com.example.app.upload.avatar.(UUID().uuidString)") config.timeoutIntervalForRequest = 60 config.timeoutIntervalForResource = 60 // 根据需求调整网络/电量策略 config.isDiscretionary = false config.sessionSendsLaunchEvents = true return URLSession(configuration: config, delegate: self, delegateQueue: nil) }()

    func uploadAvatar(image: UIImage,
                      token: String,
                      fileName: String = "avatar.jpg",
                      idempotencyKey: String? = nil,
                      completion: @escaping (Result<Data, Error>) -> Void) {
    
        // 1) 压缩到 <2MB(JPEG)
        guard let imageData = compressToUnder2MB(image: image) else {
            completion(.failure(NSError(domain: "AvatarUploader", code: -1, userInfo: [NSLocalizedDescriptionKey: "Image compression failed"])))
            return
        }
    
        // 2) 构造 multipart/form-data 文件
        let url = URL(string: "https://api.example.com/api/v1/media/avatar")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        if let key = idempotencyKey {
            request.setValue(key, forHTTPHeaderField: "Idempotency-Key")
        }
    
        // 将 multipart 数据写入临时文件以便使用背景上传
        let tempFileURL = writeMultipartToTempFile(imageData: imageData, fileName: fileName)
    
        // 3) 发起背景上传
        let task = session.uploadTask(with: request, fromFile: tempFileURL) { data, response, error in
            defer { try? FileManager.default.removeItem(at: tempFileURL) }
            if let error = error { completion(.failure(error)); return }
            guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode), let data = data else {
                let code = (response as? HTTPURLResponse)?.statusCode ?? -1
                completion(.failure(NSError(domain: "AvatarUploader", code: code, userInfo: [NSLocalizedDescriptionKey: "Upload failed"])))
                return
            }
            completion(.success(data))
        }
        task.resume()
    }
    
    private func writeMultipartToTempFile(imageData: Data, fileName: String) -> URL {
        var body = Data()
    
        // purpose
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"purpose\"\r\n\r\n".data(using: .utf8)!)
        body.append("avatar\r\n".data(using: .utf8)!)
    
        // filename field(可选字段)
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"filename\"\r\n\r\n".data(using: .utf8)!)
        body.append("\(fileName)\r\n".data(using: .utf8)!)
    
        // file
        let mime = fileName.lowercased().hasSuffix(".png") ? "image/png" : "image/jpeg"
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
        body.append("Content-Type: \(mime)\r\n\r\n".data(using: .utf8)!)
        body.append(imageData)
        body.append("\r\n".data(using: .utf8)!)
        body.append("--\(boundary)--\r\n".data(using: .utf8)!)
    
        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("avatar-\(UUID().uuidString).tmp")
        try? body.write(to: tempURL)
        return tempURL
    }
    
    private func compressToUnder2MB(image: UIImage) -> Data? {
        let maxBytes = 2 * 1024 * 1024
        var jpegQuality: CGFloat = 0.9
        guard var data = image.jpegData(compressionQuality: jpegQuality) else { return nil }
    
        // 若仍超限,逐步降低质量
        while data.count > maxBytes && jpegQuality > 0.4 {
            jpegQuality -= 0.1
            if let d = image.jpegData(compressionQuality: jpegQuality) { data = d }
            else { break }
        }
    
        // 若仍超限,按比例缩放再压缩
        if data.count > maxBytes {
            let scale = sqrt(CGFloat(maxBytes) / CGFloat(max(data.count, 1))) // 粗略缩放因子
            let targetSize = CGSize(width: max(1, image.size.width * scale),
                                    height: max(1, image.size.height * scale))
            UIGraphicsBeginImageContextWithOptions(targetSize, true, 1.0)
            image.draw(in: CGRect(origin: .zero, size: targetSize))
            let resized = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            if let resized = resized, let d = resized.jpegData(compressionQuality: 0.8) {
                return d.count <= maxBytes ? d : d // 返回尽力压缩结果
            }
        }
        return data
    }
    

    }

    // 使用示例 let uploader = AvatarUploader() uploader.uploadAvatar(image: UIImage(named: "avatar")!, token: "", fileName: "avatar.jpg", idempotencyKey: UUID().uuidString) { result in switch result { case .success(let data): // 解析 JSON if let json = try? JSONSerialization.jsonObject(with: data) { print("Upload success: (json)") } case .failure(let error): print("Upload failed:", error.localizedDescription) } }

注意事项

  • 图片要求:仅支持 JPEG/PNG,大小 ≤ 5MB;服务端会自动裁剪为正方形。为更快上传,建议客户端先压缩至 <2MB。
  • 幂等性:可设置 Idempotency-Key 请求头避免重复创建。重复的键与相同请求体将返回相同结果(不额外创建新资源)。
  • 超时与重试:建议请求超时 60s;如需重试,请复用相同 Idempotency-Key,避免产生重复头像资源。
  • 背景会话:使用 URLSession 背景上传可在 App 进入后台后继续上传;请实现相应的 URLSessionDelegate 以处理进度与完成回调。
  • 安全:仅通过 HTTPS 访问接口;响应中的 file_url 为 HTTPS 地址。
  • 客户端体验:服务端会自动正方形裁剪,建议在本地预览裁剪框以便用户确认显示区域(可选)。

示例详情

解决的问题

通过一条可复用的高质量提示词,让iOS团队以最少输入快速产出结构统一、可直接交付的接口文档。用户只需提供端点功能、目标平台与期望详细程度,即可自动生成包含功能概览、调用方式、参数说明、响应数据、异常场景、示例与注意事项的完整内容,帮助团队快速对齐,减少沟通与返工,支撑新建开发、存量梳理与评审培训,加速交付、沉淀规范并降低协作成本。

适用用户

iOS开发工程师

在新功能立项时,基于端点描述一键产出可执行文档;为复杂流程补充调用示例与注意事项;上线前快速校对参数与返回说明,减少联调反复。

后端工程师

梳理接口契约并统一格式;把需求变更快速同步为文档更新;提供清晰错误含义与边界,降低前端问答与排查时间。

测试工程师/QA

据文档生成测试要点与用例清单;覆盖常见异常与边界;在回归阶段据示例复现问题并追踪修复。

特征总结

根据端点功能一键生成规范化技术文档,结构清晰、要点齐全,减少沟通与返工成本。
自动整理概述、请求方式、参数说明、返回格式与错误含义,交付即用,团队快速对齐。
智能补充调用示例与Swift片段,贴合iOS场景,复制即用,缩短联调与自测周期。
内置清晰章节模板与排版规范,一键切换详细程度与语气,适配评审、交接与外部协作。
自动标注边界条件与注意事项,提前暴露潜在风险点,降低线上问题与修复成本。
理解业务语境并优化表述,统一术语与命名,避免歧义与遗漏,使文档更易读更可执行。
覆盖新建与维护双场景,快速增补变更项,持续保持知识库一致,支持多人协同更新。
面向电商、社交、内容等常见业务,统一口径输出,便于跨项目复用与规模复制。
通过参数化输入目标平台与粒度,一键定制生成结果,满足不同阶段的交付需求。
无需复杂培训即可上手,产品、后端、测试都能读懂,评审效率与上线速度同步提升。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 576 tokens
- 3 个可调节参数
{ 端点功能描述 } { 文档详细程度 } { 目标平台 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

半价获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59