How to call a post web service with UrlSession.DataTaskPublisher? - swift

I have a simple question that I can see only dataTaskPublisher in documentation with which I was able to call a get web service but how can I call a post web service which can return a publisher?

you can define a request and call it in your dataTaskPublisher like this :
var request = URLRequest(url: URL(string: "url")!)
let session = URLSession.shared
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! JSONSerialization.dataWithJSONObject(parameters, options: [])
session.dataTaskPublisher(for: request)

Use constructor with URLRequest and prepare request with any HTTP method you need.
/// Returns a publisher that wraps a URL session data task for a given URL request.
///
/// The publisher publishes data when the task completes, or terminates if the task fails with an error.
/// - Parameter request: The URL request for which to create a data task.
/// - Returns: A publisher that wraps a data task for the URL request.
public func dataTaskPublisher(for request: URLRequest) -> URLSession.DataTaskPublisher

Related

Why response is always {"detail":"Unsupported media type \"text/plain\" in request."} in swift?

I have created a sample app in Django which deletes a question from App. And provides a correct output when consumed using POSTMAN.
class Questions(APIView):
def delete(self,request):
received_id = request.POST["id"]
print(received_id)
place = Question.objects.get(pk=received_id)
place.delete()
questions = Question.objects.all()
seriliazer = QuestionSerializer(questions,many = True)
return Response({'Orgs': seriliazer.data})
However, when I am trying to achieve it from iOS app, it's returning {"detail":"Unsupported media type "text/plain" in request."}
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
let postString = "id=15"
request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Could not really understand where exactly the problem is ?
If the api is expecting Json, the body you are sending is not Json, it’s encoded plain text. If it should be Json you can change the body string into the Json format like:
“{\”id\”:15}”
// you may want to tell it what you’re sending
request.setValue("application/json", forHTTPHeaderField: "Accept-Encoding")
Another thing it could be is the request is missing the Accept-Encoding header which tells the api what you’re sending up where Content-Type is what the api typically sends down.
I’ve experienced header injection when I’ve sent requests through specific gateways that aren’t always right. I’d the header isn’t present, something along the way could try to help you out and add the header. This has caused me problems on the past. I still don’t know exactly where in our stack it was occurring, but adding the header fixed my problem.
You can add the header like:
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
DELETE request's body will be ignored, I could guess from the Is an entity body allowed for an HTTP DELETE request? post. HENCE Better to send the complete URL or in header itself,
so I made the function as below
def delete(self,request):
received_id = request.headers['id']
place = Question.objects.get(pk=received_id)
place.delete()
return HttpResponse("DELETE view is working fine ")
and swift
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
//let postString = "id=\(id)"
// request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
request.setValue("charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("\(id)", forHTTPHeaderField: "id")
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Shortly add Content-Type application/json in your headers
Reason
this happens because the postman has some default headers usually 8.
One of them is
Content-Type text/plain
and by writing "Content-Type": "application/json" we can overwrite that rule.
So whenever you want to pass your data like JSON do that.
to learn more what is by default in postman
I recommend you to read this official documentation of postman.
It happens with me I solved this with overwriting default Content-Type

Can timeout function be added when getting a string from a URL?

Sometimes I fetch information from a specific site.
But when the response is slow I would like to add a timeout function. I would like to know how.
Can I add a timeout function to the code below?
html = try String(contentsOf: url, encoding: String.Encoding.ascii)
You are not really supposed to use init(contentsOf:encoding:) to read from a remote URL. This initialiser is synchronous so while it is doing that your app's UI will freeze and the user won't be able to do anything, as you may have noticed.
You are supposed to use URLSession and URLRequest to fetch data from remote URLs. They are asynchronous so you get your data in a completion handler.
You can set a timeout in seconds when you create the URLRequest, and you will get an NSError in the completion handler if it timed out (among other reasons).
var request = URLRequest(url: URL(string: "https://example.com")!,timeoutInterval: 10)
request.addValue("text/plain", forHTTPHeaderField: "Content-Type")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
let result = String(data: data, encoding: .ascii)
// do something with result
}
task.resume()

How can I receive a http request in Java/Python?

I am quite new to HTTP requests, I am currently building an app which sends a question to a server, the server sends a response which the app receives and displays on the screen. This is the Swift code I use:
let url = URL(string: "https://reqres.in/api/users?page=2")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = encoded
print("started request")
URLSession.shared.dataTask(with: request) { data, response, error in
// handle the result here
}.resume()
I have already done a lot of research, but I just found code for sending an HTTP request from Java/Python, not receiving one. I am running my own PC at home which should work as a server, what code do I need in Python/Java to receive the request from Swift and respond to it?
I think that quickest way would be running spring boot app with #RestController class and #PostMapping method inside that will handle your request.
You'll need to create spring boot project with Spring Web dependency from https://start.spring.io/
Download it and add new file with controller class that will handle your rest requests.
Example controller:
#RestController
public class ExamplaryController {
#PostMapping(value = "api/users")
public ResponseEntity<WhateverTypeYouWantToBeReturned> putDataExample(#RequestParam Integer page) {
//code smth here
return result;
}
}

JWT Request Made but It tells that request does not contain access token

I tried to make a Request with JWT Authorization, The server is Using Python/Flask-Restful. The API Works on Postman, so I guess there must be something wrong with my IOS Code. The server returns an error shows that
"Authorization Required. Request does not contain an access token",
I`m making the request from IOS Using following code.
func GetUserData(username: String, accesstoken: String,completion: #escaping (_ result: UserDataModel) -> Void){
let url = URL(string: "http://********/****/\(****)")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.addValue("Authorization", forHTTPHeaderField: accesstoken)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse{
if response.statusCode != 200 {
print("Server Error When Update User Data")
} else {
if let data = data {
do {
******
completion(Data)
}
catch {
print(error)
}
}
}
}
}.resume()
}
I have no idea What is going on, Any help?
It looks like you're adding the header:
Bearer base64junk: Authorization
When instead you want:
Authorization: Bearer base64junk
You just have the parameters to addValue(_:forHTTPHeaderField:) backwards. You want this instead:
request.addValue(accesstoken, forHTTPHeaderField: "Authorization")
This should be obvious if you read that line of code like an English sentence ("value authorization for header field access token"?). In the future, you could also use something like Charles Web proxy to intercept your requests and verify that they are indeed formed the way you expect.

swift: can't set authorization header in http request

let URL: Foundation.URL = Foundation.URL(string: "MYURL")!
let request = NSMutableURLRequest(url:URL)
request.httpMethod = "GET"
request.setValue("8be4967f-dbc4-42f9-982a-30a25d9d5e12", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
As you see both Content-type and Authorization headers are set, but when server receives it, there is no Authorization in header.
But if I change Authorization word to any other random word, it works fine. (the problem consists even with Alamofire) any idea how to solve this?