0

Getting HTTP 400 error while connecting with SAP Leonardo sandbox server using scene text recognition API

SAP provides boilerplate code in older version of swift. I have used curl command and the API and works. But its not working when I try and convert the code into swift 4.2 version. I am attaching the code below.

 func connectWithSAP(photoURL : URL, photoData : String, sentImageData : Data){
          if let myNewURL = URL(string: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition") {

            var myRequest = URLRequest(url: myNewURL)
            myRequest.addValue("multipart/form-data; --\(boundary)", forHTTPHeaderField: "Content-Type")
            myRequest.addValue("application/json", forHTTPHeaderField: "Accept")
            myRequest.addValue("xxxxxxxxxxx", forHTTPHeaderField: "APIKey")
            myRequest.httpMethod = "POST"
            myRequest.cachePolicy = .reloadIgnoringLocalCacheData
            myRequest.timeoutInterval = 60.0

// Constructing the body of the request.

            var data = Data()
            var dataString = ""

            dataString.append("--\(boundary)\r\n")
            dataString.append(contentsOf: "Content-Disposition:form-data; name=\"files\"; filename=\"Image1.jpeg\" \r\n")
            dataString.append(contentsOf: ";Content-Type:image/jpeg \r\n\r\n")
            dataString.append(photoData)
            dataString.append("--\(boundary) ----- \r\n")
            data = dataString.data(using: .utf8)!
            myRequest.httpBody = data


            let task = URLSession.shared.dataTask(with: myRequest) { (data, response, error) in
            if let error = error {
                print(error)
            }
            guard let httpResponse = response as? HTTPURLResponse,
                (200...299).contains(httpResponse.statusCode) else {
                    print(error as Any)

// Getting output at this stage, which is shown below

                    return }
            if let mimeType = httpResponse.mimeType,
                mimeType == "application/json",
                let data = data {
                do {
                    let json =  try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]

                    print(json as Any)

                }catch {
                    print(error)
                }
            }

        }
        task.resume()
    }

I am getting a following details in my response object

{ URL: https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition } { Status Code: 400, Headers {\n Connection = (\n \"keep-alive\"\n );\n \"Content-Length\" = (\n 131\n );\n \"Content-Type\" = (\n \"application/json\"\n );\n Date = (\n \"Sat, 16 Feb 2019 11:56:37 GMT\"\n );\n Server = (\n \"Werkzeug/0.14.1 Python/3.5.5\"\n );\n \"Strict-Transport-Security\" = (\n \"max-age=31536000; includeSubDomains; preload;\"\n );\n \"X-Vcap-Request-Id\" = (\n \"fea7037c-4e48-49d2-4be1-53b0dad0ee46\"\n );\n}

As you would see the status code is HTTP 400. Need some help in getting the right response and data from the server.

Gunjan
  • 3
  • 2
  • Your body is malformed. Not only are there a ton of tiny syntactical errors (e.g. `\r\n` before and `--` after the final boundary, no spaces, and only two dashes; semicolon before `Content-Type`; etc.), but adding JPEG payload to a `String` type will not work and you should be dealing with `Data`, not `String`. Frankly, you might also consider using Alamofire if you want to get out of the weeds of creating multipart/form-data requests. – Rob Feb 16 '19 at 15:15

1 Answers1

0

Most probably, the body data got messed up. Here's the working code:

        let boundaryConstant = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
        let headers = [
            "APIKey": "YourAPIKEY"
        ]
        let contentType = "multipart/form-data; boundary=" + boundaryConstant
        //API endpoint for API sandbox
        var request = URLRequest(url: URL(string: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition")!)
        //setting request method
        request.httpMethod = "POST"
        request.allHTTPHeaderFields = headers
        let session = URLSession.shared


        let path1 = Bundle.main.path(forResource: "your_image", ofType: "png")!
        let url = URL(fileURLWithPath: path1)
        let fileName = url.lastPathComponent
        let data = try? Data(contentsOf: url)
        let imageData = UIImage.init(data: data!)!
        let pngData = UIImagePNGRepresentation(imageData)!
        let mimeType = "image/png"


        let boundaryStart = "--\(boundaryConstant)\r\n"
        let boundaryEnd = "--\(boundaryConstant)--\r\n"
        let fieldName = "files"
        let contentDispositionString = "Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n"
        let contentTypeString = "Content-Type: \(mimeType)\r\n\r\n"

        var body = Data()

        body.append(boundaryStart.data(using: .utf8)!)
        body.append(contentDispositionString.data(using: .utf8)!)
        body.append(contentTypeString.data(using: .utf8)!)
        body.append(pngData)
        body.append("\r\n".data(using: .utf8)!)
        body.append(boundaryEnd.data(using: .utf8)!)
        request.httpBody = body
        request.setValue(contentType, forHTTPHeaderField: "Content-Type")
        request.setValue(String(body.count), forHTTPHeaderField: "Content-Length")
        let dataTask = session.dataTask(with: request) { (data, response, error) in
            if (error != nil) {
                print(error)
            } else {
                let httpResponse = response as? HTTPURLResponse
                print(httpResponse)
            }
        }
        dataTask.resume()

You can also use Alamofire to upload image. It's way cleaner, don't need to play around with "body" much:

        let headers: HTTPHeaders = [
            "APIKey": "<<Your API KEY>>",
            "Content-type": "multipart/form-data"
        ]
        let parameters:[String: String] = [:] //any other parameters you need to  send

        let path1 = Bundle.main.path(forResource: "<<your_image>>", ofType: "<<png or jpeg>>")!
        let url = URL(fileURLWithPath: path1)
        let fileName = url.lastPathComponent
        let data = try? Data(contentsOf: url)
        let imageData = UIImage.init(data: data!)!

        //converting it into png data
        let pngData = UIImagePNGRepresentation(imageData)
        let mimeType = "image/png"
        let fieldName = "files" 

        Alamofire.upload(multipartFormData: { (multipartFormData) in
            for (key, value) in parameters {
                multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
            }

            if let data = pngData{
                multipartFormData.append(data, withName: fieldName, fileName: fileName, mimeType: mimeType)
            }

        }, usingThreshold: UInt64.init(), to: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition" , method: .post, headers: headers) { (result) in
            switch result{
            case .success(let upload, _, _):
                upload.responseJSON { response in
                    print("Succesfully uploaded")


                }
            case .failure(let error):
                print("Error in upload: \(error.localizedDescription)")

            }
        }
Rikesh Subedi
  • 1,755
  • 22
  • 21
  • Thanks RIkesh, I tried this but it is now giving me HTTP error code 405. I am taking a picture using my phone's camera and then passing that information to the API. Any help will be appreciated. – Gunjan Feb 16 '19 at 22:00
  • @Gunjan, The above code worked well in my case. It was able to extract out text from the image. Please check if all the parameters are fine. Also you can try the Alamofire version of code, I have just added. – Rikesh Subedi Feb 17 '19 at 02:21
  • @Gunjan, one small suggestion, use web debugging tools such as Charles Proxy to find out what is going wrong in the network. – Rikesh Subedi Feb 17 '19 at 02:25
  • Thanks @Rikesh, it worked after putting the file in the Bundle folder. Appreciate your help. – Gunjan Feb 17 '19 at 05:59
  • @Gunjan: You're welcome, glad it helps – Rikesh Subedi Feb 21 '19 at 15:24