APIClient: URL.appending(path:) frisst Query-Strings — String-Concat statt
URL.appending(path:) behandelt den Input als reinen Path-Component und percent-encoded Sonderzeichen, also auch ?. Damit wurde aus /api/dogs/123/diary?limit=50 ein /api/dogs/123/diary%3Flimit=50, und der Server lieferte was Anderes als JSON zurück → 'data was not valid JSON'. Betraf auch Wetter, Giftköder, Verlorene Hunde, Gassi-Zeiten und alle anderen Endpoints mit Query. Jetzt: baseURL.absoluteString + path.
This commit is contained in:
parent
3373305b23
commit
12f8ba0be8
1 changed files with 10 additions and 4 deletions
|
|
@ -42,7 +42,7 @@ final class APIClient {
|
|||
mimeType: String = "image/jpeg"
|
||||
) async throws -> Data {
|
||||
let boundary = "Boundary-\(UUID().uuidString)"
|
||||
let url = baseURL.appending(path: path)
|
||||
let url = Self.makeURL(baseURL: baseURL, path: path)
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "POST"
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
|
@ -69,7 +69,7 @@ final class APIClient {
|
|||
}
|
||||
|
||||
private func perform<T: Decodable>(method: String, path: String, body: Data?) async throws -> T {
|
||||
let url = baseURL.appending(path: path)
|
||||
let url = Self.makeURL(baseURL: baseURL, path: path)
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = method
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
|
@ -98,7 +98,7 @@ final class APIClient {
|
|||
|
||||
/// Convenience for DELETE with no response body.
|
||||
func delete(_ path: String) async throws {
|
||||
let url = baseURL.appending(path: path)
|
||||
let url = Self.makeURL(baseURL: baseURL, path: path)
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "DELETE"
|
||||
if let token { req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") }
|
||||
|
|
@ -116,7 +116,7 @@ final class APIClient {
|
|||
/// Convenience for PATCH with JSON body, decoding the response.
|
||||
func patch<T: Decodable, B: Encodable>(_ path: String, body: B) async throws -> T {
|
||||
let data = try encoder.encode(body)
|
||||
let url = baseURL.appending(path: path)
|
||||
let url = Self.makeURL(baseURL: baseURL, path: path)
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "PATCH"
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
|
@ -135,6 +135,12 @@ final class APIClient {
|
|||
return try decoder.decode(T.self, from: respData)
|
||||
}
|
||||
|
||||
/// Constructs a URL by joining baseURL and path as strings — `URL.appending(path:)`
|
||||
/// percent-encodes `?` and breaks query strings.
|
||||
private static func makeURL(baseURL: URL, path: String) -> URL {
|
||||
URL(string: baseURL.absoluteString + path) ?? baseURL
|
||||
}
|
||||
|
||||
/// FastAPI returns errors as {"detail": "message"} or {"detail": [{...}]}.
|
||||
private static func parseErrorDetail(from data: Data) -> String? {
|
||||
if let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue