AFNetworking 2.0 sends parameters and image to server - ios

I am using AFNetworking2 to send parameters and image to server.
manager.POST(urlPath, parameters: parameters, constructingBodyWithBlock: { (formData: AFMultipartFormData!) -> Void in
formData.appendPartWithFileData(imageData!, name: "image", fileName: "dummy.jpg", mimeType: "image/jpeg")
}, success: { (dataTask: NSURLSessionDataTask!, responseObject: AnyObject!) -> Void in
println("success: \(responseObject.description)")
}, failure: { (dataTask: NSURLSessionDataTask!, error: NSError!) -> Void in
println("failure: \(error)")
})
On server side, the data will be a dictionary merged by parameters(QueryDict) and the image data(MultiValueDict):
data=MergeDict(<QueryDict: {u'owner': [u'6'], u'description': [u'this
is p1'], u'name': [u'p1']}>, <MultiValueDict: {u'image':
[<InMemoryUploadedFile: file.jpg (image/jpeg)>]}>)
I reckon the 'MultiValueDict' is from this part of code:
formData.appendPartWithFileData(imageData!, name: "image", fileName: "dummy.jpg", mimeType: "image/jpeg")
However, I wanted to have MultiValueDict like this:
{u'groupImages': [{u'image': [<InMemoryUploadedFile: file.jpg (image/jpeg)>]}]}
The data format is a Dictionary with an array value, and the array has another Dictionary value.
So what can I do to make formData.appendPartWithFileData become such above data format?
EDIT:
I have seen some posts similar to my question.
For example this one: AFNetworking post image in nested json
I have tried to change my code like this:
formData.appendPartWithFileData(imageData!, name: "groupImages[0].image", fileName: "dummy.jpg", mimeType: "image/jpeg")
or
formData.appendPartWithFileData(imageData!, name: "groupImages[0][image]", fileName: "dummy.jpg", mimeType: "image/jpeg")
but none of them worked for me.
My server expects to receive a JSON like this:
{
"name": "p2",
"owner": 6,
"description": "this is p2",
"groupImages": [{
"image": <InMemoryUploadedFile: dummy.jpg (image/jpeg)>
}]
}
Any idea?

When you send a multipart HTTP request, the JSON data and the image data are separate - literally in multiple parts. It might be worth taking a look at this answer to "What is HTTP Multipart Request" so you can see how the data is transmitted.
The long and short of it is that the JSON and the image are merged into a dictionary on the server end. The image is not transmitted embedded within the JSON. The semantics of how they're merged (for example, how a name like groupImages[0][image] is used to merge in with the JSON dictionary) are determined by the server, not by your iOS app.
So, your server team should be able to specify how you name this file so that it's merged with the dictionary correctly. They should be able to produce a sample HTTP request that works properly (for example, using curl or Postman). If your server has a web app, you could inspect the analogous function in the web app to see what the request looks like there. Once you have a working request, you can mimic it by comparing your outgoing NSURLRequest to the sample request they provide.

Related

KrakenD: Trouble to upload file though gateway via POST request using Form-Data

Describe what are you trying to do
In one of my applications, I need to upload a file to my server from my angular website.
Basically, to do this I use the FormData object to which append several informations, like the file name, and others.
To send the file itself I will append to the FormData an fs.readStream().
Then I post this via axios to my server endpoint.
Exemple of code (postman request using form-data):
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('avatar', fs.createReadStream('/home/file.mp3'));
data.append('title', 'test');
data.append('description', 'test');
var config = {
method: 'post',
url: 'localhost:8080/upload-file',
headers: {
...data.getHeaders()
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Concerning the server, it is developed in node.js and I use the "multer" middleware to retrieve the file.
Exemple of an endpoint code :
import {Response, Request} from "express";
public static async UploadFile(req: Request, res: Response): Promise<any> { }
Without krakend gateway, it's works perfectly and I can then retrieve the file in my endpoint so that: req.file
The others informations sent like "title", "description" are in req.body
Using krakend, I get all the information on the server side except the file, in the request, I only find the req.body and not the req.file
So my question is, how come krakend is not sending the file data to the backend and what would be the solution in order to send file via POST request a FormData to krakend ?
Your configuration file
The content of your krakend.json:
{
"version": 3,
...
{
"endpoint": "/upload",
"method": "POST",
"output_encoding": "no-op",
"backend": [
{
"method": "POST",
"encoding": "no-op",
"url_pattern": "/upload-file",
"host": [
"http://containername:8080"
]
}
]
}
}
I tried to use the different "no-op" annotations but nothing works, I have the impression that krakend does not interpret my file upload
Commands used
How did you start the software?
I use docker-compose:
krakend:
container_name: 'Gateway'
image: devopsfaith/krakend
volumes:
- ./KrakenD/dev/:/etc/krakend
ports:
- "8080:8080"
- "1234:1234"
- "8090:8090"
links:
- some containers
- ...
restart: always
network_mode: bridge
Logs
I don't have a specific log, only my backend which returns a 400 code as it can't find the file information in the request.
for those who are going through the same problem. Please note that postman was the cause of the problem for me, KrakenD does support sending bodies as multipart/form-data, so don't forget to let the headers through as needed.
The problem is that when passing through postman, I can't explain but the file is badly sent to krakenD. Use Insomnia, or a simple Curl to do your tests.
Maybe you have to include "Content-Type" at input-headers.
"input_headers": [
"Authorization",
"Content-Type"
],
Here you can find more info about it.

F# with Http.fs - not able to execute GraphQL APIs

I don't see any good documentation about how to execute GraphQL APIs using F# with Http.fs
Kindly share if you have the correct syntax available or point to the correct documentation for the same. I was trying with the Star Wars API given here: https://www.rithmschool.com/blog/an-introduction-to-graphql-queries
URL: https://swapi.graph.cool
Header: 'Content-Type': 'application/json'
JSON Body:
query {
Film (title:"A New Hope" ) {
director
characters {
name
}
}
}
Expected Response same as: https://swapi.graph.cool/
I'm not familiar with Http.fs, but here is a small working example of calling the API using the F# Data Http utility:
Http.RequestString
( "https://swapi.graph.cool",
httpMethod="POST", headers=[ HttpRequestHeaders.ContentType("application/json") ],
body=TextRequest("{\"query\": \"{ allFilms { title } }\"}") )
The main thing is that the body needs to be a JSON value where the actual query is a string stored in a record with a field named "query", i.e. {"query": "...."}.

fluentd in http input json parser how to send multiple data in one point?

according to json parser docs https://docs.fluentd.org/v0.12/articles/parser_json
The json parser plugin parses json logs. One JSON map, par line.
when sending one json at a time everything working fine:
$.post('<server_ip>/log',
{json:JSON.stringify({
"name":"test",
})});
(i am using in_http source)
but when trying to send it a multipile points at once it always fail with
400 Bad Request 757: unexpected token
$.post('<server_ip>/log',
{
json: JSON.stringify({
"name": "test",
}) + "\n" + JSON.stringify({
"name": "test2",
})
});
edit:
the solution is:
$.post('<server_ip>/log','json=[{"name":"test"},{"name":"test2"}]');
Use batch mode with default format: https://docs.fluentd.org/v0.12/articles/in_http#batch-mode

In Swift, How to have a json request automatically cancel itself if the data returned is of a different format?

I'm running into this problem where JSON data downloaded from a site is of a different format compared to other data received from the same site. This problem is causing the app to freeze. For example 98% percent of the data received from the site looks like this:
{
id: 27673,
title: "Unbreakable Kimmy Schmidt",
alternate_titles: [ ],
status: "",
type: "online",
container_show: 0,
first_aired: "2015-02-17",
network: ""
}
but for some strange reason some of the JSON data is formatted like this:
{
id: 35107,
title: "Jessica Jones",
alternate_titles: [ ],
status: "",
type: "online",
container_show: 0,
first_aired: false,
network: ""
}
The value for "first_aired" is a string for the first JSON data but a Bool for the second. Is there to make the JSON data automatically cancel the request if the format of the JSON data changes and go back to the original view it was on before the request was made? Or even better just skip over the value that is different than the original format but still retrieve the rest of the data?
Caleb's method worked. I removed the optional binding from all the JSON data and instead have it set up like this:
let firstAired = jsonResult["first_aired"] as? String ?? "N/A"
It worked like a charm. The app doesn't freeze anymore and when the JSON data is of a different format it reads "N/A" instead.

Is Possible to POST array of dictionaries in Afnetworking POST Method?

I am developing one iOS application that Post data to web service and Get data from the Web service.
I am using Afnetworking for Post the data to web service.Web service accept the data in the form of array of dictionaries like
[{"name":"stephen","age":25},{"name":"john","age":35},{"name":"david","age":45},{"name":"roger","age":15}]
which need to send in the body of the request and set Accept application/json in the header of the request.
I checked some examples already avilable in the stack overflow but all are explain about Post dictionary .My question is possible to Post array to web service using Afnetworking. If it is possible help me to develop the code which completely reach my requirements.
If your are sending all dictionaries in single array. Very first you have to JSONserialize every dictionary and then put it in array and then again JSON serialise the whole array. Then only it will be called as a correct JSON format. Its the only way you can do it.
My suggestion for you is to use AFJSONRequestSerializer.
Swift solution:
let manager = AFHTTPSessionManager(baseURL: URL(string: yourBaseURL))
manager.requestSerializer = AFJSONRequestSerializer()
let parameters: [[String: Any]] = [["name": "stephen", "age": 25], ["name": "john", "age": 35],["name": "david", "age": 45]]
manager.post(requestURL,
parameters: parameters,
progress: nil,
success: { (task, data) in
// process server response
}, failure: { (task, error) in
// process error server response
})
AFJSONRequestSerializer sets Content-type to application/json in request header.

Resources