HTTP DELETE Works From Browser But Not From Postman or IOS App - ios

When attempting an http request to my rest api, I continually get a 401 error when using the following code. I don not get this error making any other type of request. I have provided the function that makes the request below.
func deleteEvent(id: Int){
eventUrl.append(String(id))
let request = NSMutableURLRequest(url: NSURL(string: eventUrl)! as URL)
request.httpMethod = "DELETE"
print(eventUrl)
eventUrl.removeLast()
print(self.token!)
request.allHTTPHeaderFields = ["Authorization": "Token \(self.token)"]
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
if error != nil {
print("error=\(String(describing: error))")
//put variable that triggers error try again view here
return
}
print("response = \(String(describing: response))")
}
task.resume()
}
When sending the delete request with postman, the rest api just returns the data I want to delete but does not delete it. For reference I have posted the view and permissions classes associated with this request Any help understanding why this may be resulting in an error is greatly appreciated!
Views.py
class UserProfileFeedViewSet(viewsets.ModelViewSet):
"""Handles creating, reading and updating profile feed items"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.ProfileFeedItemSerializer
queryset = models.ProfileFeedItem.objects.all()
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
Permissions.py
class UpdateOwnStatus(permissions.BasePermission):
"""Allow users to update their own status"""
def has_object_permission(self, request, view, obj):
"""Check the user is trying to update their own status"""
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile.id == request.user.id
HEADER SENT WITH DELETE REQUEST VIA POSTMAN

Preface: You leave out too much relevant information from the question for it to be properly answered. Your Swift code looks, and please don't be offended, a bit beginner-ish or as if it had been migrated from Objective-C without much experience.
I don't know why POSTMAN fails, but I see some red flags in the Swift code you might want to look into to figure out why your iOS app fails.
I first noticed that eventUrl seems to be a String property of the type that contains the deleteEvent function. You mutate it by appending the event id, construct a URL from it (weirdly, see below), then mutate it back again. While this in itself is not necessarily wrong, it might open the doors for racing conditions depending how your app works overall.
More importantly: Does your eventUrl end in a "/"? I assume your DELETE endpoint is of the form https://somedomain.com/some/path/<id>, right? Now if eventUrl just contains https://somedomain.com/some/path your code constructs https://somedomain.com/some/path<id>. The last dash is missing, which definitely throws your backend off (how I cannot say, as that depends how the path is resolved in your server app).
It's hard to say what else is going from from the iOS app, but other than this potential pitfall I'd really recommend using proper Swift types where possible. Here's a cleaned up version of your method, hopefully that helps you a bit when debugging:
func deleteEvent(id: Int) {
guard let baseUrl = URL(string: eventUrl), let token = token else {
// add more error handling code here and/or put a breakpoint here to inspect
print("Could not create proper eventUrl or token is nil!")
return
}
let deletionUrl = baseUrl.appendingPathComponent("\(id)")
print("Deletion URL with appended id: \(deletionUrl.absoluteString)")
var request = URLRequest(url: deletionUrl)
request.httpMethod = "DELETE"
print(token) // ensure this is correct
request.allHTTPHeaderFields = ["Authorization": "Token \(token)"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Encountered network error: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse {
// this is basically also debugging code
print("Endpoint responded with status: \(httpResponse.statusCode)")
print(" with headers:\n\(httpResponse.allHeaderFields)")
}
// Debug output of the data:
if let data = data {
let payloadAsSimpleString = String(data: data, encoding: .utf8) ?? "(can't parse payload)"
print("Response contains payload\n\(payloadAsSimpleString)")
}
}
task.resume()
}
This is obviously still limited in terms of error handling, etc., but a little more swifty and contains more console output that will hopefully be helpful.
The last important thing is that you have to ensure iOS does not simply block your request due to Apple Transport Security: Make sure your plist has the expected entries if needed (see also here for a quick intro).

Related

login user with GET request swift

I have created a screen with a text field called customer_number text field and another screen with a text field called password text field. I want to integrate my app with an existing API made by the backend developers. I am new to IOS Development and I don't know how to go about it. How do I make a get request and pass the login credentials for the user to login?
I want to get the customer number from the API and pass it to the app and enable the customer to log in.
I think this question is too big and complex to be replied exhaustively. You didn't tell us about the API. What kind of input does it take? What kind of response?
Supposing the simplest case. You API expects JSON objects as input and respond with another JSON object containing the information you request.
I usually do tasks like this using the NSURLRequest.
let js = ["Username":username, "Password":password]
let session = URLSession.init(configuration: .default)
let url = URL(...)
var req = URLRequest.init(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10)
req.httpMethod = "POST"
// Add some header key-value pairs
req.addValue(..., forHTTPHeaderField: ...)
...
let task = session.dataTask(with: request) { (data, response, error) in
guard error == nil else { return }
guard let responseData = data else { return }
let code = (response as! HTTPURLResponse).statusCode
// Checking for code == 200 states for authorised user. Generally log-in APIs should return some 4xx code if not allowed or non-authorised user.
if code == 200 {
// Now we try to convert returned data as a JSON object
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
// use your json object here, for example checking if contains the user number...
} catch {
// handle errors
}
}
}
task.resume()
I coded this very quickly, please check the correctness of al mechanism!

URLSession.Datatask returns 0 bytes of data

Trying to figure this one out, I'm stumped. When making a REST call to get json data back from a response (GET or POST, each should return data) I get back 0 bytes.
This is pre-serialization. The POST successfully creates a message on the backend, and the backend shows a response being sent; with charles proxy on, I've confirmed that there is a response with valid JSON data.
Any ideas why this would be failing ONLY in iOS? Postman/Charles proxy (from the iOS calls!) shows valid data in the response, but the debugger picks up nothing.
Thanks in advance for anything thoughts.
let components = URLComponents(string: "mysuperValidURL.com")
guard let url = components?.url else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
setUrlRequestToken(request: &request)
let message = ChatMessage(content: message, group: group, userId: userId)
let jsonEncoder = JSONEncoder()
guard let data = try? jsonEncoder.encode(message) else {
return
}
URLSession.shared.uploadTask(with: request, from: data) { (data, response, error) in
// Here there be 0 bytes
}.resume()
}
Data will sometimes come back as 0 bytes in the debugger; add a print with debug description to ensure you're getting data. In this case it was a failure of the debugger mixed with a later serialization error that caused it to appear to be broken.
TLDR; don't trust the realtime debugger, use some prints to sanity check.

http request delete and put

Below is my code for HTTP request for getting and post. What I wanted to know is how to do HTTP request for delete and put. I have made it possible for get and post to work. I want to know how on the part of delete and put based on my code below. what to change in my postcode when I want to change it to delete? what is lacking? I wanted to delete using ID
like for example "id": 16,
let parameters = ["name": "test", "desc": "test" , "reward":"1.00" , "sched":"2018-04-05T11:49:51+08:00", "occurrence":["name": "once"]
, "status": "created", "created_by": "test#gmail.com","created_for": "test.com"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
get
guard let url = URL(string: "http://test.test:8000/api/v1/test") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
The only one that differs is GET with the parameters are in the url itself , POST & DELETE & PUT are work the same way , just change the httpMethod parameter and specify httpBody if you wanted to , meaning
DELETE : means delete a resource from a specific url
PUT : place a resource in to a web server
//
let parameters = ["ID": "16"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject:parameters, options: []) else { return }
request.httpBody = httpBody
You should go through difference between each method types. It will help you, what should you do and when.
PUT
Store an entity at a URI. PUT can create a new entity or update an existing one. A PUT request is idempotent. Idempotency is the main difference between the expectations of PUT versus a POST request.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request. If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.
Modify the address with an ID of 1:
PUT /addresses/1
Note: PUT replaces an existing entity. If only a subset of data elements are provided, the rest will be replaced with empty or null.
urlRequestInstance.httpMethod = "PUT"
DELETE
Request that a resource be removed; however, the resource does not have to be removed immediately. It could be an asynchronous or long-running request.
The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.
A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity.
Delete an address with an ID of 1:
DELETE /addresses/1
urlRequestInstance.httpMethod = "DELETE"
Here are nice tutorial references for you:
Understanding REST
REST Methods
What is the usefulness of PUT and DELETE HTTP request methods?
Now you can try a sample code, answered by Sh_Khan

How can I pull the grades on the website, and perform the login using a POST request?

I am trying to pull my school grades from the website which stores all my grades, but I am having trouble logging in using HTTP requests, and pulling the information of the next page. Any help is appreciated :)
override func viewDidLoad() {
super.viewDidLoad()
let myUrl = NSURL(string: "https://homeaccess.katyisd.org/HomeAccess/Account/LogOn?ReturnUrl=%2fhomeaccess%2f")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "User_Name=**hidden**&Password=**hidden**"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data,response,error in
if(error != nil){
print("error=\(error)")
return
}
print("response = \(response)")
// Print out response body
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
//Let’s convert response sent from a server side script to a NSDictionary object:
do{
var myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
if let parseJSON = myJSON {
// Now we can access value of First Name by its key
var firstNameValue = parseJSON["User_Name"] as? String
print("firstNameValue: \(firstNameValue)")
}
}catch{
print(error)
}
}
}
First, you need task.resume() after defining the task in order to start the connection loading, otherwise the object will be created and nothing will actually happen.
According to this error you posted, there's an SSL verification error on the site you are trying to access. The most secure option is to fix the SSL on the site, but I presume that is beyond your control in this case. The easier fix is to bypass the SSL error by adding "App Transport Security Settings" > "Allow Arbitrary Loads" = YES in your info.plist, as #André suggested. Or, if you are only using the one domain, bypass the particular domain in the NSExceptionDomains. See this question for more info.
According to this error you posted, a JSON parsing error is occurring. It is currently being caught and printed by your catch block, so the data is not actually processed. In your case, this is occurring because the response from Home Access Center is HTML, not JSON, so the JSON parser is failing. You are probably looking for an HTML parser. Swift does not have one built-in; look at this question for some example open-source options.
I have actually created a program that interfaces with Home Access Center. Sadly, there is no public API available -- APIs typically return JSON, which is easier to work with. Instead, you will need to use an HTML parser to analyze the page that is meant for human users and cookies to fake that a human user is logging on.
add task.resume() at the end of your code. also add the following to your info.plist file:

Swift NSJSONSerialization.JSONObjectWithData() not able to read data for "OK" String only

This might be the strangest thing I have encountered. We have decided to move on from it but I wanted to make a post to try and understand.
So I am grabbing some JSON data from our server and everything seems to work just fine except for the string "OK".
Here is the function :
func getRequest(token:String, url:String, callback:(NSDictionary) -> ()){
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "GET"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if data != nil && response != nil{
do{
let responseTest = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
if responseTest != nil{
let response:NSDictionary = responseTest!
callback(response)
}
} catch let error as NSError{
print("A JSON parsing error occured: \(error)")
}
} else {
callback(NSDictionary())
}
}
task.resume()
}
The data comes back form the call and then the response parses all of the data correctly except for the status "OK" which came back with unable to read data. We changed the status to "Success" and it worked just fine. We tested out a bunch of different words and they all worked just fine except the word "OK" which always came back with Unable to read data. Strangest thing I have every seen. For now we are going to go with a status of "Success" but, I just can't get this out of my head. WHY?!?! What is different about those 2 letters? Anyone else run into this or just have an understanding that I seem to be lacking? It is driving me crazy.
Here is the JSON from one of our attempts:
{"username":"gang_su","status":"0K","status2":"This is much more OK","status3":"OK OK OK","status4":"OK","status5":true,"status6":123,"status7":12345.678,"status8":[1,2,3,4],"status9":[1.02,2.02,3.02,4.02]}
Thanks!!
{ username: req.user.username, status: 'OK', status2: "This is much more OK" }
The above "JSON" has single quotes around OK. This is invalid JSON.
It's not OK, you might say.
They need to be double quotes, as you have used in your other strings.
I just tested this in a playground, and it works fine with your provided example JSON. The logical conclusion is that that's not actually the data you're passing in.
You might want to dump that NSData to the console, or to a file, and check for any unexpected characters.

Resources