Upload image into asp.net web service in swift - ios

How can I upload an image into my web service then I can save it on my server!
I have tried the below code using POST method but I got this error
(A potentially dangerous Request.Form value was detected from the client (uploadFile="<ffd8ffe0 00104a46 4...").)
func myImageUploadRequest() {
let imageData = UIImageJPEGRepresentation(myImageView, 1)
let boundary = generateBoundaryString()
var base64String = imageData.base64EncodedStringWithOptions(.allZeros)
let myUrl = NSURL(string: "http://xxxxx/UploadImageTest");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
if(imageData==nil) { return; }
var body = NSMutableData();
body.appendString("uploadFile=\(imageData)")
request.HTTPBody = body
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
// You can print out response object
println("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("****** response data = \(responseString!)")
dispatch_async(dispatch_get_main_queue(),{
});
}
task.resume()
}
And this the POST SOAP on my Asp.net web service
POST /xxxx.asmx/UploadImageTest HTTP/1.1
Host: xxxx.com
Content-Type: application/x-www-form-urlencoded Content-Length: length
uploadFile=string&uploadFile=string

The error was sending NSData to Asp.net web service.
convert the image int base64 using this code
let imageData = UIImageJPEGRepresentation(myImageView, 1)
var base64String = imageData.base64EncodedStringWithOptions(.allZeros)
then send it to web service and make sure in your web service receive String data, not byte() array.
in your web service convert the base64 into Image and save into your server.
That's it!.

I am not familiar with Swift but in Objective C I would have prepared data something like this:
....
NSMutableString* body = [NSMutableString new];
[body appendFormat:#"uploadFile=%#",base64String];
....
The reason it is erring out is pretty obvious from the error description. You might find your code is formatting your base64 image string as
uploadFile="<ffd8ffe0 00104a46 4...
The text after uploadFile= is not at all a base64 encoded string rather the string representation of NSData and that extra < at the starting of the data is being treated as a html tag in server. ASP.NET request validation does not allow such tags in your request body as security measure, to prevent code/script injection and cross site scripting.
Even if you disable request validation from server web.config or .asmx the data would still not be interpreted by server as it would still not be in valid base64 format as the server might expect.
So my advice is to frame your request properly before sending it to server and everything should work seamlessly.

Related

HTTP Post Request data could not be read Swift 3

I've been trying to get data by Http "POST" method.In my php script i have a key call "categoryWise" which has a value called "flower".I put all the necessary codes but it doesn't work and says The data couldn’t be read because it isn’t in the correct format.Please help.
let values = "categoryWise= nature"
let parameter = values.data(using: .utf8)
let url = "https://mahadehasancom.000webhostapp.com/WallpaperApp/php_scripts/getImageByCategory.php"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.httpBody = parameter
request.setValue("application/x-content-type-options", forHTTPHeaderField: "Content-Type")
request.setValue("application/x-content-type-options", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if (error != nil)
{
print(error!)
}
else
{
do
{
let fetchData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
//print(fetchData)
let actorArray = fetchData?["result"] as? NSArray
for actor in actorArray!
{
let nameDict = actor as? NSDictionary
let name = nameDict?["date"] as! String
let countryname = nameDict?["category"] as! String
let imageUrl = nameDict?["url"] as! String
//let pageUrl = nameDict?["url"] as! String
authorArray.append(name)
titleArray.append(countryname)
imageURL.append(imageUrl)
//urlArray.append(pageUrl)
}
DispatchQueue.main.async {
self.CountryNameTable.reloadData()
}
print(authorArray)
print(titleArray)
print(imageURL)
print(urlArray)
}
catch let Error2
{
print(Error2.localizedDescription)
if let string = String(data: data!, encoding: .utf8)
{
print(string)
print(response!)
}
}
}
}
task.resume()
A few observations:
You shared PHP that is using $_POST. That means it's expecting x-www-form-urlencoded request. So, in Swift, you should set Content-Type of the request to be application/x-www-form-urlencoded because that's what you're sending. Likewise, in Swift, the Accept of the request should be application/json because your code will "accept" (or expect) a JSON response.
The values string you've supplied has a space in it. There can be no spaces in the key-value pairs that you send in a x-www-form-urlencoded request. (Note, if you have any non-alphanumeric characters in your values key pairs, you should be percent encoding them.)
In your Swift error handler, in addition to printing the error, you might want to try converting the data to a String, and looking to see what it says, e.g.
if let string = String(data: data!, encoding: .utf8) {
print(string)
}
You might also want to look at response and see what statusCode it reported. Having done that, you subsequently told us that it reported a statusCode of 500.
Status code 500 means that there was some internal error in the web service. (The code is 200 if successful.) This is generally a result of some error with the request not being handled correctly. For example, if the request neglected to format the request correctly and the web service doesn't anticipate/catch that, or if there was some other internal error on the web server, you could get 500 error code. For list of status codes, see http://w3.org/Protocols/rfc2616/rfc2616-sec10.html.
If the text in the body of the response from your web service is not illuminating, you might want to turn on error reporting (see How to get useful error messages in PHP? or How do I catch a PHP Fatal Error) and then look at the body of the response again. For example, you might include the following in your PHP:
<?php
function __fatalHandler() {
$error = error_get_last();
//check if it's a core/fatal error, otherwise it's a normal shutdown
if ($error !== NULL && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING))) {
header("Content-Type: application/json");
$result = Array("success" => false, "error" => $error);
echo json_encode($result);
die;
}
}
register_shutdown_function('__fatalHandler');
// the rest of your PHP here
?>

How to get a MS Translator access token from Swift 3?

I am trying to work with a MS Translator API from Swift 3 (right now playing in playgrounds, but the target platform is iOS). However, I got stuck when I was trying to get an access token for OAuth2. I have following code (I tried to port the code from example at Obtaining an access token):
let clientId = "id".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let clientSecret = "secret".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let scope = "http://api.microsofttranslator.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let translatorAccessURI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
let requestDetails = "grant_type=client_credentials&client_id=\(clientId)&client_secret=\(clientSecret)&scope=\(scope)"
let postData = requestDetails.data(using: .ascii)!
let postLength = postData.count
var request = URLRequest(url: URL(string: translatorAccessURI)!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("\(postLength)", forHTTPHeaderField: "Content-Length")
request.httpBody = postData
URLSession.shared.dataTask(with: webRequest) { (returnedData, response, error) in
let data = String(data: returnedData!, encoding: .ascii)
print(data)
print("**************")
print(response)
print("**************")
print(error)
}.resume()
Of course, I used a valid clientId and a valid clientSecret.
Now the callback prints following information. First, the returnedData contain a message that the request was invalid, along with a following message:
"ACS90004: The request is not properly formatted."
Second, the response comes with a 400 code (which fits the fact that the request is not properly formatted).
Third, the error is nil.
Now I was testing the call using Postman, and when I used the same URI, and put the requestDetails string as a raw body message (I added the Content-Type header manually), I got the same response. However, when I changed the body type in Postman UI to application/x-www-form-urlencoded and typed in the request details as key value pairs through its UI, the call succeeded. Now it seems that I am doing something wrong with the message formatting, or maybe even something bad with the Swift URLRequest/URLSession API, however, I cannot get a hold on to what. Can somebody help me out, please? Thanks.
OK, so after some more desperate googling and experimenting I have found my error. For the future generations:
The problem resided in encoding the parameters in the body of the PUT http request. Instead of:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
I have to use the following:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters:
CharacterSet(charactersIn: ";/?:#&=$+{}<>,").inverted)!
Seems that the API (or the HTTP protocol, I am not an expert in this) have problems with / and : characters in the request body. I have to give credit to Studiosus' answer on Polyglot issue report.

iOS swift post request with binary body

I want to make a POST request from iOS (swift3) which passes a chunk of raw bytes as the body. I had done some experimenting which made me thought the following worked:
let url = URL(string: "https://bla/foo/bar")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(hex: "600DF00D")
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
"DATA \(data ?? Data()) RESPONSE \(response) ERROR \(error)".print()
}
task.resume()
Didn't know it was a problem until I tried sending something simple like a single 0xF0. At which point my tornado server started complaining that I was sending it
WARNING:tornado.general:Invalid x-www-form-urlencoded body: 'utf-8' codec can't decode byte 0xf0 in position 2: invalid continuation byte
Am I just supposed to set some header somehow? Or is there something different I need to do?
The two common solutions are:
Your error message tells us that the web service is expecting a x-www-form-urlencoded request (e.g. key=value) and in for the value, you can perform a base-64 encoding of the binary payload.
Unfortunately, base-64 strings still need to be percent escaped (because web servers generally parse + characters as spaces), so you have to do something like:
let base64Encoded = data
.base64EncodedString(options: [])
.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
.data(using: String.Encoding.utf8)!
var body = "key=".data(using: .utf8)!
body.append(base64Encoded)
var request = URLRequest(url: url)
request.httpBody = body
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print(error!)
return
}
...
}
task.resume()
Where:
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
return allowed
}()
}
For more discussion on that character set, see point 2 in this answer: https://stackoverflow.com/a/35912606/1271826.
Anyway, when you receive this on your server, you can retrieve it as and then reverse the base-64 encoding, and you'll have your original binary payload.
Alternatively, you can use multipart/formdata request (in which you can supply binary payload, but you have to wrap it in as part of the broader multipart/formdata format). See https://stackoverflow.com/a/26163136/1271826 if you want to do this yourself.
For both of these approaches, libraries like Alamofire make it even easier, getting you out of the weeds of constructing these requests.

Trouble with NSURLSession multipart/form-data post request

Im having some trouble with sending text and images in the same post request to my server. I think the problem has to do with the way i set my boundary.
Im using swift with ios9.
I followed instructions here ios Upload Image and Text using HTTP POST
doing my best to convert the obj-c into swift
however when i post a request to my server, whenever i try to access the post data such as $_POST["key"] i get an undefined index error. Here is the code i use to setup the http request, can anyone spot the error:
func sendRegisterRequest(params:Dictionary<String, String>, withImage image: UIImage) {
// https://stackoverflow.com/questions/8564833/ios-upload-image-and-text-using-http-post
let url = NSURL(string: "MY_URL");
let request = NSMutableURLRequest(URL: url!)
// the boundary string : a random string, that will not repeat in post data, to separate post data fields.
let boundaryConstant = "----------V2ymHFg03ehbqgZCaKO6jy--";
// string constant for the post parameter 'file'. My server uses this name: `file`. Your's may differ
let fileParamConstant = "file";
request.HTTPMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundaryConstant)", forHTTPHeaderField: "Content-Type")
// post body
let body = NSMutableData();
// add params (all params are strings)
for (key, value) in params {
body.appendData("\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData("Content-Disposition: form-data; name=\"\(key.stringByAddingPercentEncodingWithAllowedCharacters(.symbolCharacterSet())!)\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData("\(value.stringByAddingPercentEncodingWithAllowedCharacters(.symbolCharacterSet())!)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
}
//print(body);
// add image data
let imageData = UIImageJPEGRepresentation(image, 1.0);
body.appendData("\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData("Content-Disposition: form-data; name=\"\(fileParamConstant)\"; filename=\"image\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData(imageData!);
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
body.appendData("\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!);
request.HTTPBody = body;
When you use multipart form data, you must prefix the boundary with an additional two hyphens within the body data, and you must also add two hyphens at the end of the final boundary. So if you have:
boundary=foo
then the body should look like this:
--foo
field 1 info
--foo
field 2 info
--foo--
See also What is the '-' in multipart/form-data?

Swift POST request delivers body to node.js/express application in wrong format

I am trying to send a JSON object from an ios app using Swift. Everything works but the fact that the body in the request body on the node.js/express backend is in an aca-awkward format where the whole JSON object parsed in Swift is the actual key. So what is being received on the server is:
req.body = { '{"email":"email","password":"password"}': '' }
I want it to be:
{ "email":"email","password":"password" }
So I can access the key-values with, for example, req.body.email
I am new to http communication between ios and nodejs so maybe this is
normal but its very annoying.
My ios http post code is :
let url = NSURL(string: "http://localhost:3000/users")
let request = NSMutableURLRequest(URL:url!)
request.HTTPMethod = "POST"
var login_details: [String: AnyObject] = [
"email" : "\(self.email_field.text)",
"password" : "\(self.password_field.text)"
]
let valid = NSJSONSerialization.isValidJSONObject(login_details)
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(login_details, options: nil, error: &err)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
}
My node router module is:
router.post('/', function(req, res) {
console.log(req.body);
});
Node automatically parses the JSON request body to object only if it knows that it is the right content type. You have two possible solutions, use one or the other but not both since they conflict.
1) client side, more correct IMO. Set the right content type. Add this to your Swift code:
request.setValue("application/json" forHTTPHeaderField:"Content-Type")
2) server side. Just parse the body yourself:
parsedBody = JSON.parse(body)
// parsedBody will now be an object

Resources