2

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 HEADER SENT WITH DELETE REQUEST VIA POSTMAN

TJ D'Alessandro
  • 109
  • 1
  • 19
  • Error 401 is Unauthorized Error. Check the header field `Authorization` to see if it's sent correctly. Depending on the auth type the API is using, you may need to reformat it. Usually `Authorization: Bearer {{TOKEN}}` is a common approach. Try using CURL or Postman to see if you can make a request through there, to ensure the API is working. – Ishan Joshi Jul 13 '20 at 00:52
  • @IshanJoshi I've tried using postman and I am able to contact the api as an authenticated user but I am not able to delete any content even though the api is set up to do so. Any thought on what the problem could be (could it be with my permissions/views files in the rest framework code) – TJ D'Alessandro Jul 14 '20 at 14:05
  • Compare the requests headers – Ohad the Lad Jul 15 '20 at 13:50
  • @TJD'Alessandro that's great to hear you can call the API using postman. The API may not be functioning as expected? Is the API something that you can control? – Ishan Joshi Jul 16 '20 at 01:17
  • @IshanJoshi yes, I have wrote the API and have provided some of the code for it above – TJ D'Alessandro Jul 16 '20 at 01:37
  • @OhadtheLad I've edited the post to add the header that is sent with the delete request using postman – TJ D'Alessandro Jul 16 '20 at 01:55
  • If authorisation is working correctly, then there must be an issue with the API – Shashank Mishra Jul 19 '20 at 06:24
  • Are you sure you parse "Token13411111111" and get only desired token string from it? Post your server method dealing with it. – ares777 Jul 20 '20 at 10:21

1 Answers1

1

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).

Gero
  • 4,394
  • 20
  • 36