I'm creating an app using flutter, in this I'm creating a post request then reading the response as string (later to convert into json).
Future<String> post([String url = "https://example.com/get-new"]) async {
return await http.post(Uri.encodeFull(url),
headers: {"Accept": "application/json"}).then((http.Response response) {
final int statusCode = response.statusCode;
if (statusCode < 200 || statusCode > 400) {
throw new Exception("Error while fetching data");
}
return response.body;
});
}
post().then((val){
print(val);
});
It's working fine and getting the response, but as my question says, response is limited to 1023 characters. I checked it with postman and found the response is coming properly as needed.
It would be great if anyone would help me out to solve this issue.
Thanks in advance
Related
Something I'm running into when using Vapor to crawl sites is if I enter a url like http://website(dot)com and it redirects to https://www.website(dot)com, I need to grab that final response URL to know what the final page URL is.
I can do this with URLSession but I'm not seeing something similar in the Vapor client. I tried using the Vapor client and the async-http package it's built on and I can't find the page URL listed in the response.
Any pointers would be appreciated, thank you.
Example getting this info using URLSession
if let url = URL(string: self) {
do {
let(_, response) = try await URLSession.shared.data(from: url)
if let responseURL = response.url {
result = responseURL.description
return result
}
} catch {
}
}
Where this is the first line of the response, and you can easily use response.url to grab the below "URL" value:
<NSHTTPURLResponse: 0x600000210c00> { URL: https://github.com/ } { Status Code: 200, Headers {
This question already has answers here:
Alamofire Response Serialization Failed
(2 answers)
Closed 6 months ago.
I have an API where I PUT stuff to. I need to make sure to wait until I get an http 200 response from the server, but I don't know how to await that using Alamofire because my response itself if empty. So it's just an http 200 with no content.
I only can find async functions that e.g. serialize a String or Data or a Decodable, but they don't work if my response is empty.
Is there a way to await something like that in Alamofire?
I know that your question is about async/await from Alamofire, but is good to know that the http status codes 204 and 205 are exactly for this. Which means that if you have access to the server code you could send the empty responses with the http status code 204 and 205 instead of 200 and then Alamofire would not generate any errors. But assuming you don't have access to the server code and you need to parse an empty response as correct then you could use the following code:
func testRequestWithAlamofire() {
let dataResponseSerializer = DataResponseSerializer(emptyResponseCodes: [200, 204, 205]) // Default is [204, 205] so add 200 too :P
AF.request("http://www.mocky.io/v2/5aa696133100001335e716e0", method: .put).response(responseSerializer: dataResponseSerializer) { response in
switch response.result {
case .failure(let error):
print(error)
case .success(let value):
print(value)
}
}
}
And for a real and complete example of how async/await from Alamofire or any other async context look this code:
// This function get report from API and save to a local JSON to be readed by the app
func updateReport() {
Task {
guard let session = self.sessionRepository.getSession(WithUser: Defaults.lastLoggedUsername!) else { return }
guard let company = session.profile?.companies.first else { return }
self.apiManager.configure(WithToken: session.accessToken)
do {
let dateA = Date().dateAtStartOf(.year)
//let dateB = Date().dateAtEndOf(.month)
let dateB = Date() // Just now
let report = try await self.apiManager.report(CompanyId: company._id, DateA: dateA, DateB: dateB, ChartPeriodicity: .month)
self.currentReport = report
// Save data to disk to be read later
self.reportManager.saveReportToDisk(report: report!, withProfileId: session.profile!._id)
} catch {
print("Error getting report: \(error)")
}
}
}
// Get personal report from a given date range
func report(CompanyId companyId: String, DateA dateA: Date, DateB dateB: Date, ChartPeriodicity chartPeriodicity: ChartPeriodicity) async throws -> CDReport? {
try await withCheckedThrowingContinuation { continuation in
self.contappApi.request(.report(companyId: companyId, dateA: dateA, dateB: dateB, chartPeriodicity: chartPeriodicity)) { result in
switch result {
case let .success(response):
// Check status code
guard response.statusCode == 200 else {
continuation.resume(throwing: ContappNetworkError.unexpected(code: response.statusCode))
return
}
// Decode data
do {
//let report = try JSONDecoder().decode(CDReport.self, from: response.data)
let report = try CDReport(data: response.data)
continuation.resume(returning: report)
} catch {
continuation.resume(throwing: ContappNetworkError.cantDecodeDataFromNetwork)
}
case .failure(_):
continuation.resume(throwing: ContappNetworkError.networkError)
}
}
}
}
Alamofire already supports this, you just need to choose a form. Your biggest issue will be accepting a 200 with no data, as that's technically invalid since only 204 or 205 are supposed to be empty.
All Alamofire responses require some sort of payload type, but Alamofire provides an Empty type to fill this role for Decodable. So the simplest way is to use the
await AF.request(...)
.serializingDecodable(Empty.self, emptyResponseCodes: [200])
.response
Note, if you already have an Empty type or are importing Combine in the same file as this code, you may need to disambiguate by using Alamofire.Empty.
If Alamofire does not provide a method for your purpose, then you will have wrap the old Alamofire methods that uses closures as below:
func myRequest() async throws {
try await withUnsafeThrowingContinuation { continuation in
myAlamofireRequest {
continuation.resume()
}
}
}
I have an API client that uses generic API response that conforms to Codable Protocol and uses JSONDecoder to decode the response as shown below, how do I handle having a response which doesn't return JSON ( status code 201 created)?
dataRequest.validate().responseJSON { response in
if let error = response.error {
completion(.failure(error.localizedDescription))
} else if let data = response.data {
do {
let apiResponse = try JSONDecoder().decode(T.Response.self, from: data)
completion(.success(apiResponse))
} catch {
completion(.failure(error.localizedDescription))
}
} else {
completion(.failure("Something went wrong, please try again later."))
}
}
It returns this error:
the response could not be serialized input data was nil or zero-length
In this case you can look at the statusCode property of the response (assuming that it is a HTTPURLResponse) and make your determination about whether or not there will be a body to parse. I would put it immediately after the error check.
I'm using swiftHTTP for requesting to my server and when my internet connection is slow, it goes to response part! I've set the example code below:
HTTP.GET("myURL") { response in
let myResponse = response.data // it comes here after the timeout
if response.statusCode == 200 {
//some code
} else {
do {
let jsonError = try JSON(data: myResponse) // in this line it goes to catch because there is no data in myresponse
} catch{
//alert for not having connection
}
}
Why does it call the response function if there's no response?
My server also says, that no request was sent.
It doesn't "go to response", it tries to make the HTTP request as expected and regardless of success or error it's completion handler is called.
The response object that is returned is an object that contains all of the information you need to determine what happened with the request.
So it will contain a timeout status code (HTTP 408), possibly an error message. If it did not respond at all, your app would not be able to handle these cases.
Say for example your user taps on their profile icon in the app and this sends a request to get the users profile, but it timed out. Do you want the user sat waiting, looking at a blank profile screen? It's much better to catch the error and handle it gracefully. In this case you could present a message to the user telling them that something went wrong and close the empty profile screen
Your response handler will also be called from swiftHTTP, if there's no or a very bad connection.To solve this problem, either check if there is an internet connection or check if the data is nil:
HTTP.GET("myURL") { response in
let myResponse = response.data // it comes here after the timeout
if response.statusCode == 200 || response.data == nil {
//some code
} else {
do {
let jsonError = try JSON(data: myResponse) // in this line it goes to catch because there is no data in myresponse
} catch{
//alert for not having connection
}
}
The important part here is the check if response.data == nil.
I have a problem I want to handle a JSON Response with Alamofire in Swift
So I found this answer on Stackoverflow unfortunately this post is a few days older.
My question is how can I receive the data from Alamofire on first button press (without swiftyJSON).
I hope someone could help me.
This is the link I found on Stackoverflow.
Handle JSON Response with Alamofire in Swift
this is a small example.
This is what the JSON returns if it fails json_file.json
{ "transaction":"error" }
This is what the JSON returns if its success json_file.json
{ "transaction":"success" }
this is the code , you must add your own URL that will return any of those json responses. (example only)
Alamofire.request(.GET, "http://myjsonexamplewebsite.com/json_file.json", parameters:nil)
.responseJSON { (_, _, JSON, _) in
//println(JSON)
var response = JSON as NSDictionary
var transaction = response.objectForKey("transaction") as String
if transaction == "success" {
NSLog("JSON response was successfull")
}
else {
NSLog("JSON response had an Error")
}
}