im fairly new and facing a problem about sending api request - swift

I'm fairly new to the swift and I'm facing a problem with Alamofire what I'm trying to do is sending a get request with json body but I'm stuck heres my code:
struct request {
func getMessage(message:String){
let parameters: [String:Any] = [
"message" : [
"text": message,
"type":"text"
],
"type":"message",
"recipentId":"",
"info": [
"token":""
]
]
makeRequest(parameters: parameters)
}
func makeRequest(parameters:[String:Any]){
AF.request("https://bot.ortus4c.com/api/botController/token/5cc1f42e66d0ba0001dbf944",method: .post,parameters: parameters).response{
response in
debugPrint(response)
}
}
after executing this code i get:
[Request]: POST https://bot.ortus4c.com/api/botController/token/5cc1f42e66d0ba0001dbf944
[Request Body]:
info%5Btoken%5D=&message%5Btype%5D=text&message%5Btext%5D=Karakter&recipentId=&type=message
[Response]:
[Status Code]: 415
[Headers]:
Accept: application/json
Content-Length: 0
Date: Sat, 07 Mar 2020 19:10:23 GMT
Server: cloudflare
cf-cache-status: DYNAMIC
cf-ray: 57069dc3eaccad06-OTP
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
x-b3-traceid: b10ba988c0249901
[Response Body]:
None
[Data]: None
[Network Duration]: 0.3303520679473877s
[Serialization Duration]: 0.0s
[Result]: success(nil)
i dont get any result body compared to what expect when i send this request over postman:
"result": {
"messages": [
{
"type": "text",
"text": "Karakter Analizine Hoşgeldiniz. Analize başlamak için \"karakter\" yazınız"
},
{
"type": "text",
"text": "Test"
}
],
"tag": [
"start",
"finish"
],
"conversation_id": "5e63f302dc4bec0001883f01",
"intent_name": "Default Karakter",
"intent_id": "5ccb46ef66d0ba0001dbfcdf",
"intent_score": null,
"current_flow_id": "ee52db21-dae1-4587-80cd-7649798dddce1583608578222"
}
so how can i send this post request with this given body below:
let parameters: [String:Any] = [
"message" : [
"text": message,
"type":"text"
],
"type":"message",
"recipentId":"",
"info": [
"token":""
]
]
Now i have sent the request and got a body looking like this:
"result": {
"messages": [
{
"extra": {},
"question": "Adınız ?",
"type": "text",
"values": [
{
"text": ""
}
],
"askCount": 1,
"text": "Adınız ?",
"waitUserInput": true
}
],
"tag": [
"start",
"p_firstName",
1
],
"conversation_id": "5e63ffa0dc4bec0001883f10",
"intent_name": "Karakter Analizi",
"intent_id": "5cc20ae266d0ba0001dbf946",
"intent_score": "62.0",
"current_flow_id": "62ac3ac8-0cd7-43e5-b92b-f119d49c40d41583611808850"
}
how can i print the question variable to a label on my screen ?

Response code 415 means Unsupported Media Type and the Accept header of the response lets you know that the sever expects data to be sent as JSON. All you have to do is to let Alamofire know that it should encode parameters as JSON in the request body
AF.request("https://bot.ortus4c.com/api/botController/token/5cc1f42e66d0ba0001dbf944",
method: .post,
parameters: parameters,
encoding: JSONEncoding.default).response { response in
debugPrint(response)
}

you need to mention the encoding type as well while sending request. since your parameters data is json add encoding parameter as encoding: JSONEncoding.default
for example i have used as:
manager.request(url, method: httpMethod,
parameters: parameters,
encoding: JSONEncoding.default,
headers: configuredHeader)
.validate(statusCode: 200..<300)
.responseJSON(completionHandler: { [weak self] response in
self?.handleNetworkResponse(response: response, networkCompletionHandler: networkCompletionHandler)
})

Related

URLSessionDataTask ignores URLRequest's httpMethod property

I'm having this bizarre problem in which I cannot do a simple POST request to a REST service I do not control based on GraphQL.
The problem is that no matter what I set in the httpMethod property of the URLRequest class, it always uses GET instead.
I have done a few tests to discard some problems. For example, I set up a header in the Request and I can verify that the header is being sent to the server (verified with Charles Proxy).
This is the code you can paste and run in a Playground:
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution = true
let url = URL(string: "http://graphql.anilist.co/")!
let internalSession = URLSession(configuration: .default)
var request = URLRequest(url: url)
request.httpMethod = "POST"
let headers = ["content-type": "application/json"]
request.allHTTPHeaderFields = headers
request.httpBody =
"""
{"query":"query (\n\t$season: MediaSeason,\n\t$year: Int,\n\t$format: MediaFormat,\n\t$excludeFormat: MediaFormat,\n\t$status: MediaStatus,\n\t$minEpisodes: Int,\n\t$page: Int,\n){\n\tPage(page: $page) {\n\t\tpageInfo {\n\t\t\thasNextPage\n\t\t\ttotal\n\t\t}\n\t\tmedia(\n\t\t\tseason: $season\n\t\t\tseasonYear: $year\n\t\t\tformat: $format,\n\t\t\tformat_not: $excludeFormat,\n\t\t\tstatus: $status,\n\t\t\tepisodes_greater: $minEpisodes,\n\t\t\tisAdult: false,\n\t\t\ttype: ANIME,\n\t\t\tsort: TITLE_ROMAJI,\n\t\t) {\n\t\t\t\nid\nidMal\ntitle {\n\tromaji\n\tnative\n\tenglish\n}\nstartDate {\n\tyear\n\tmonth\n\tday\n}\nendDate {\n\tyear\n\tmonth\n\tday\n}\nstatus\nseason\nformat\ngenres\nsynonyms\nduration\npopularity\nepisodes\nsource(version: 2)\ncountryOfOrigin\nhashtag\naverageScore\nsiteUrl\ndescription\nbannerImage\ncoverImage {\n\textraLarge\n\tcolor\n}\ntrailer {\n\tid\n\tsite\n\tthumbnail\n}\nexternalLinks {\n\tsite\n\turl\n}\nrankings {\n\trank\n\ttype\n\tseason\n\tallTime\n}\nstudios(isMain: true) {\n\tnodes {\n\t\tid\n\t\tname\n\t\tsiteUrl\n\t}\n}\nrelations {\n\tedges {\n\t\trelationType(version: 2)\n\t\tnode {\n\t\t\tid\n\t\t\ttitle {\n\t\t\t\tromaji\n\t\t\t\tnative\n\t\t\t\tenglish\n\t\t\t}\n\t\t\tsiteUrl\n\t\t}\n\t}\n}\n\nairingSchedule(\n\tnotYetAired: true\n\tperPage: 2\n) {\n\tnodes {\n\t\tepisode\n\t\tairingAt\n\t}\n}\n\n\t\t}\n\t}\n}","variables":{"season": WINTER,"year": 2019,"page": 1, "perPage": 100}}
""".data(using: .utf8)
print("THE REQUEST \(String(describing: request.httpMethod))")
let task = internalSession.dataTask(with: request, completionHandler: { (data, response, error) in
if let e = error {
print("ERROR: \(e)")
} else if let response = response as? HTTPURLResponse {
print("THE RESPONSE: \(response)")
let json = try! JSONSerialization.jsonObject(with: data!, options: [])
print(json)
}
})
task.resume()
(Please ignore all the forced unwrap optionals and forced try!, this is test code).
Expected Result
I expect the web service to return a JSON similar to this (simplified):
{
"data": {
"Page": {
"pageInfo": {
"hasNextPage": true,
"total": 81
},
"media": [
{
"id": 102882,
"idMal": 37956,
"title": {
"romaji": "3D Kanojo: Real Girl 2",
"native": "3D彼女 リアルガール 2",
"english": "Real Girl 2"
},
"startDate": {
"year": 2019,
"month": 1,
"day": 9
},
"endDate": {
"year": null,
"month": null,
"day": null
},
"status": "RELEASING",
"season": "WINTER",
"format": "TV",
"genres": [
"Romance",
"Slice of Life",
"Comedy"
],
"synonyms": [],
"duration": 23,
"popularity": 3298,
"episodes": 12,
"source": "MANGA",
"countryOfOrigin": "JP",
"hashtag": "#3D彼女リアルガール #3D彼女",
"averageScore": 61,
"siteUrl": "https://anilist.co/anime/102882",
"description": "The second season of <i>3D Kanojo</i>.",
"bannerImage": null,
"coverImage": {
"extraLarge": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx102882-lKp3ExWNzoE6.jpg",
"color": "#e4bb93"
},
"trailer": {
"id": "x6yokzp",
"site": "dailymotion",
"thumbnail": "https://www.dailymotion.com/thumbnail/video/x6yokzp"
},
"externalLinks": [
{
"site": "Official Site",
"url": "http://www.3dkanojo-anime.com/"
},
{
"site": "Twitter",
"url": "https://twitter.com/3Dkanojo_anime"
}
],
"rankings": [
{
"rank": 16,
"type": "RATED",
"season": null,
"allTime": false
},
{
"rank": 19,
"type": "POPULAR",
"season": null,
"allTime": false
},
{
"rank": 16,
"type": "RATED",
"season": "WINTER",
"allTime": false
},
{
"rank": 15,
"type": "POPULAR",
"season": "WINTER",
"allTime": false
}
],
"studios": {
"nodes": [
{
"id": 346,
"name": "Hoods Entertainment",
"siteUrl": "https://anilist.co/studio/346"
}
]
},
"relations": {
"edges": [
{
"relationType": "PREQUEL",
"node": {
"id": 100526,
"title": {
"romaji": "3D Kanojo: Real Girl",
"native": "3D彼女 リアルガール",
"english": "Real Girl"
},
"siteUrl": "https://anilist.co/anime/100526"
}
},
{
"relationType": "SOURCE",
"node": {
"id": 80767,
"title": {
"romaji": "3D Kanojo: Real Girl",
"native": "3D彼女 〈リアルガール〉",
"english": "Real Girl"
},
"siteUrl": "https://anilist.co/manga/80767"
}
}
]
},
"airingSchedule": {
"nodes": [
{
"episode": 7,
"airingAt": 1550595540
},
{
"episode": 8,
"airingAt": 1551200340
}
]
}
}
]
}
}
}
Obtained Result
Instead, I get this:
{
"data": null,
"errors": [
{
"message": "Not Found.",
"hint": "Use POST request to access graphql subdomain.",
"status": 404
}
]
}
The service complains that the request is a GET request and should be POST, and I am explicitly telling URLRequest to use POST instead. If you look at the request in Charles or another proxy, you will indeed see that the request is being done as a GET request, and the httpBody property is being discarded. If you edit the headers and add another header, like so:
let headers = ["content-type": "application/json", "foo": "bar"]
You will see in your proxy that the header is being properly sent.
The only conclusion I can arrive is that there is an internal problem with URLSessionDataTask that forces to do GET requests only. I tried changing it to to a download task, but the same problem is happening. So there must be a problem with my code, but I cannot find it.
EDIT:
Per request, this is the request Postman uses. I have exported the request to CURL to make it easily importable.
curl -X POST \
https://graphql.anilist.co/ \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 49d7e55e-35c2-4a3c-82f0-4eccb1250fc0' \
-H 'cache-control: no-cache' \
-d '{"query":"query (\n\t$season: MediaSeason,\n\t$year: Int,\n\t$format: MediaFormat,\n\t$excludeFormat: MediaFormat,\n\t$status: MediaStatus,\n\t$minEpisodes: Int,\n\t$page: Int,\n){\n\tPage(page: $page) {\n\t\tpageInfo {\n\t\t\thasNextPage\n\t\t\ttotal\n\t\t}\n\t\tmedia(\n\t\t\tseason: $season\n\t\t\tseasonYear: $year\n\t\t\tformat: $format,\n\t\t\tformat_not: $excludeFormat,\n\t\t\tstatus: $status,\n\t\t\tepisodes_greater: $minEpisodes,\n\t\t\tisAdult: false,\n\t\t\ttype: ANIME,\n\t\t\tsort: TITLE_ROMAJI,\n\t\t) {\n\t\t\t\nid\nidMal\ntitle {\n\tromaji\n\tnative\n\tenglish\n}\nstartDate {\n\tyear\n\tmonth\n\tday\n}\nendDate {\n\tyear\n\tmonth\n\tday\n}\nstatus\nseason\nformat\ngenres\nsynonyms\nduration\npopularity\nepisodes\nsource(version: 2)\ncountryOfOrigin\nhashtag\naverageScore\nsiteUrl\ndescription\nbannerImage\ncoverImage {\n\textraLarge\n\tcolor\n}\ntrailer {\n\tid\n\tsite\n\tthumbnail\n}\nexternalLinks {\n\tsite\n\turl\n}\nrankings {\n\trank\n\ttype\n\tseason\n\tallTime\n}\nstudios(isMain: true) {\n\tnodes {\n\t\tid\n\t\tname\n\t\tsiteUrl\n\t}\n}\nrelations {\n\tedges {\n\t\trelationType(version: 2)\n\t\tnode {\n\t\t\tid\n\t\t\ttitle {\n\t\t\t\tromaji\n\t\t\t\tnative\n\t\t\t\tenglish\n\t\t\t}\n\t\t\tsiteUrl\n\t\t}\n\t}\n}\n\nairingSchedule(\n\tnotYetAired: true\n\tperPage: 2\n) {\n\tnodes {\n\t\tepisode\n\t\tairingAt\n\t}\n}\n\n\t\t}\n\t}\n}","variables":{"year":2019,"season":"WINTER","page":1, "limit": 12}}'
Only difference I am seeing here is http:// in your Playground and https:// in your Postman.
So just replace http:// with https:// in your Playground.

Mapping by request's body use two matches

I has http request with body:
endpoint = http://127.0.0.1:54400/json
reqBody:
{
"action": "Handler:GET_DICTIONARY",
"locale": "ro",
"data": {"dictionary_type":"MTS"}
}
I need to get stubbed response.
Here my Wiremock mapping:
{
"request": {
"method": "POST",
"bodyPatterns": [
{
"contains": "Handler:GET_DICTIONARY"
}
]
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"fixedDelayMilliseconds": 3000,
"bodyFileName": "t2a/micb/webclient/_mts_response.json"
}
}
But I has many another requests that content request's body with text:
"Handler:GET_DICTIONARY"
So as result I need to mapping also by
"dictionary_type":"MTS"
because text
and
"dictionary_type":"MTS" AND "Handler:GET_DICTIONARY" create UNIQUE request.
So how I can mapping by request's body use this two matches?
I would suggest to add "matchesJsonPath" in addition to your "contains"
"bodyPatterns": [
{
"matchesJsonPath": "$.data[?(#.dictionary_type == 'MTS')]"
}
]
That will guarantee that all the request with dictionary_type = MTS will mapped to that response.

Wiremock - How can I apply response templating to the header name?

I am trying to provide a MOCK service that takes a headerName and value from the query and returns it as a (dynamic) header with the response. I am using the following response definition:
"response" : {
"status" : 200,
"statusMessage": "OK",
"headers" : {
"Content-Type" : "application/json",
"{{request.query.debugHeader}}" : "{{request.query.debugHeaderValue}}"
},
"jsonBody" : {
"headerSent": "{{request.query.debugHeader}} {{request.query.debugHeaderValue}}"
},
"transformers": ["response-template"],
"base64Body" : ""
}
The header value is correctly evaluated and put into the response template, however I can't get the header name to be taken from the request.
When sending a request:
http://localhost:8090/example?debugHeader=name&debugHeaderValue=value
The result headers I get back are:
HTTP/1.1 200 OK
Content-Type: application/json
{{request.query.debugHeader}}: value
However I want {{request.query.debugHeader}} to be replaced with the actual request parameter value ("name" in the example above).
Any ideas?
Thanks in advance
Alex
This is supported in WireMock.Net.
The request which you need to specify looks like this:
{
"Guid": "90356dba-b36c-469a-a17e-669cd84f1f05",
"Priority": 0,
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/trans",
"IgnoreCase": false
}
]
},
"Methods": [
"get"
]
},
"Response": {
"StatusCode": 200,
"BodyDestination": "SameAsSource",
"Body": "{\"msg\": \"Hello world : {{request.path}}\" }",
"UseTransformer": true,
"Headers": {
"Content-Type": "application/json",
"Transformed-Postman-Token_{{request.path}}": "token is {{request.headers.Postman-Token}}"
}
}
}
This will add the transformed header Transformed-Postman-Token_{{request.path}} in the response.
Presently this type of variability is not part of the out-of-the-box WireMock application and would have to be custom added. In the WireMock documentation the section Extending WireMock and in particular the part on Transforming Responses.

Swift - Alamofire "Invalid value around character 0."

I found some thread the same problem but their answers not works for me.
I have a post request to check user login.
Url form :
"http://support.xxx.xx:8031/serpapi/login/checkLogin"
Parameters :
["contextInfo" :
["clientId": "1000000", "orgId": "1000001",
"warehouseId": "1000002", "roleId": "0"],
"userName": "hanoiaUser", "password": "hanoiaUser"]
Request :
Alamofire.request(url!, method: .post, parameters: params).responseJSON { (response) in
print(response)
}
Error response :
FAILURE: responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}))
Why am i getting this error? and how to solve it ?
Edit : i'm tried with POSTMAN and it return response :
Content-Type : application/json
Body : raw - json
{
"contextInfo" : {
"clientId":1000000,
"orgId": 1000001,
"warehouseId": 1000002,
"roleId": 0
},
"userName": "hanoiaUser",
"password": "hanoiaUser"
}
RESPONSE :
{
"success": true,
"data": [
{
"userId": 1000003,
"userName": "hanoiaUser",
"token": "b7e804d25065e5c3ac97d765180b7986"
}
],
"error": null
}
Ok, finally I solved the problem
Here's new request :
Alamofire.request(url!, method: .post,encoding : JSONEncoding.default, headers: headers, parameters: params).responseJSON { (response) in
print(response)
}
With Headers :
let headers = [
"Content-Type": "application/json"
]
Set the request header like :
request.allHTTPHeaderFields = ["Content-Type":"application/json"]

Alamofire sending object as parameter

I'm having hard time doing something simple. The data I want to send is the following:
{
"nickname":"Rado",
"social": {
"data: {
"accesstoken":"xx",
"applicationId":"xx",
"userId":"xx"
},
"type":"whatever"
}
}
Currently I'm doing that:
let params = [
"nickname": userName,
"social": [
"type": "whatever",
"data": [
"userId": accessToken.userID,
"accesstoken": accessToken.tokenString,
"applicationId": accessToken.appID
]
]
]
Alamofire.request(.POST, "url/users", parameters: params, headers: nil)
.responseJSON { response in
}
As a response I get this:
{
"nickname":"Rado",
"social[data][userId]":"xx",
"social[data][applicationId]":"xx",
"social[data][accesstoken]":"xx",
"social[type]":"something"
}
Any advice will be appreciated!
The solution turned out to be really simple. I was missing encoding: .JSON
Alamofire.request(.POST, "url/users", parameters: params, headers: nil, encoding: .JSON)
.responseJSON { response in
}