How to read the body of the request in Azure App Service? - ios

I am trying to create an EasyAPI in Azure App Service (migrated from Azure Mobiles Services). The message is sent from an iOS app using the following command in swift:
let query: Dictionary<String, AnyObject> = ["name": theName]
let param: Dictionary<String, AnyObject> = ["collectionName": theCollectionName, "query": query]
AOAppDelegate.client!.invokeAPI("FMDataAPI", body: param, HTTPMethod: "POST", parameters: nil, headers: nil, completion: {(objects, httpResponse, error) in
if error == nil {
//Process response
} else {
print(error!.userInfo)
}
})
In the API I have the following Javascript code within the EasyAPI MyEasyAPI:
module.exports = {
"post": function (req, res, next) {
console.log("---------------------------------------")
console.log(req.body)
},
But body stays undefined.
Any suggestions?
Thanks,
GA

You need to adjust the app before the easy API gets called. By the time you are adding the middleware, it's too late. Fortunately, bodyparser is already implemented for you. Note that generally, you need to be doing something that expects a body - like a POST - to do this.
Since this is a migrated mobile service, you need to follow the instructions for mobile services - things that are documented for App Service are generally only available for upgraded sites (that is, sites running on App Service that have not been migrated).
Good news is that we have some help there if you need it. Check out the node module: https://github.com/Azure/azure-mobile-apps-node-compatibility for more information.

Thanks for your suggestions.
I reported an issue in the github project of the Azure Mobile Apps iOS client and they suggested that the problem could be in the version of the node package azure-mobile-apps and this was the case.
The azure-mobile-apps package was in version 2.0.0. After I updated to 2.1.0 req.body started to receive data.
Here you have the link to the Gihub issue discussion.
Thanks again for your suggestions.
GA

I had a similar use case with my custom API and fetched the parameter data via the query statement - I used a GET call, but that shouldn't make any difference here:
module.exports = {
get: function (req, res, next) {
var param = req.query.completed;
console.log(param);
}
};
The call with the attached parameter dictionary on the iOS side looked as follows:
[self.client invokeAPI:#"resetMyItems"
body:nil
HTTPMethod:#"GET"
parameters:#{#"completed": #(self.completed)}
headers:nil
completion:^(id result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
NSLog(#"Got answer from my own API: %#", result);
}
else {
NSLog(#"Something went wrong with POST api call: %#", error);
}
}
];
I searched quite a long time to find out, that the parameter you attach on your API call on the iOS side is called EXACTLY the same as on the query statement on your request.
Hope that helps :)

Related

Stuck with Api response Ktor

I am trying to build a KMM application using Ktor for our ApiServices. I have created a BaseApiClass where I have all of the api related code.
Code for BaseApiClass :-
class BaseAPIClass {
//Create Http Client
private val httpClient by lazy {
HttpClient {
defaultRequest {
host = ApiEndPoints.Base.url
contentType(ContentType.Application.Json)
header(CONNECTION, CLOSE)
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
install(HttpTimeout) {
requestTimeoutMillis = NETWORK_REQUEST_TIMEOUT
}
expectSuccess = false
// JSON Deserializer
install(JsonFeature) {
val json = Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
serializer = KotlinxSerializer(json)
}
}
}
// Api Calling Functions I have few more similar to this but issue is random and comes in any of the api
#Throws(Exception::class)
suspend fun sampleApi(requestBody: RequestBody?) : Either<CustomException, BaseResponse<EmptyResponseModel>> {
return try {
val response = httpClient.post<BaseResponse<EmptyResponseModel>> {
url(ApiEndPoints.sample.url)
if (requestBody != null) {
body = requestBody
}
}
Success(response)
}
catch (e: Exception) {
Failure(e as CustomException)
}
}
Here's how I call the api from iOS app :-
val apiClass = BaseApiClass()
func callApi() {
apiClass.sampleApi(requestBody: .init(string: "value here")) { (result, error) in
result?.fold(failed: { (error) -> Any? in
// Error here
}, succeeded: { (result) -> Any? in
// Success here
})
}
}
Now here if I try to call similar few more api's with the same object i.e apiClass then after few calls it get stuck inside my function callApi it don't send even api request (Because I can't see Request Logs printed in my console) and because of that I cannot do any other operations as I don't get anything from api.
As soon as I change my screen or close the app and try to call the same api then it works good.
But instead of creating a object only at one time like this apiClass = BaseApiClass() if I try to do with BaseApiClass().sampleApi(request params here) {// completion handler here} it works fine I don't get any issues with this.
I am not sure what causes this to happen everything works good in Android this is faced only with iOS.
Try to set LogLevel.NONE in the install(Logging) block.
At the moment I resolved in this way because it seems a bug of Ktor.
See: https://youtrack.jetbrains.com/issue/KTOR-2711
It should be fixed in the version 1.6.0.
Are you using the multithreaded variant of the Coroutines library? The official docs state that you should use this variant when working with Ktor. See here
After all the efforts and trying a lot of debugging skills I got to understand that my completion handler in the shared module is never called even if I receive the response the response from api.
The only solution I have achieved is creating the different HTTP Client using expect and actual mechanism. By making separate clients I have not encountered the issue yet.
If you have any other answers or solutions I would be happy to have a look at it.

Parse PFCloud.callInBackground completion block never called i.e no response received

I have defined a function in the Parse Cloud Code called "relatedWords". When I try call this function in my iOS app, the completion block/closure is never called i.e no response is received.
I've tested the function in the Parse API Console and it is working fine there, so I know it's not an issue with the cloud code.
Any ideas on what the issue is?
My swift code:
func fetchRelatedKeyWordsForWord(word: String)
{
PFCloud.callFunctionInBackground("relatedWords", withParameters: ["hashtag": word]) { (response, error) -> Void in
//This is never called
print(response)
print(error)
}
}
Snippet of the cloud code:
Parse.Cloud.define("relatedWords", function(request, response) {
Parse.Cloud.useMasterKey();
var hashtag = request.params.hashtag;
...
...
//Run a query
var query = new Parse.Query(parseClassName);
query.find({
success: function(results) {
if (results.length != 0) {
console.log("Found Objects! Returning Objects");
response.success(results);
return;
}
Edit:
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem.
you can use other server, other vise pay money on parse and solve the problem.

LinkedIn SDK ios swift

I am trying to integrate LinkedIn SDK in iOS using swift
I found the below code in objective-C (https://developer.linkedin.com/docs/signin-with-linkedin)
NSString *url = [NSString initWithString:#"https://api.linkedin.com/v1/people/~"];
if ([LISDKSessionManager hasValidSession]) {
[[LISDKAPIHelper sharedInstance] getRequest:url
success:^(LISDKAPIResponse *response) {
// do something with response
}
error:^(LISDKAPIError *apiError) {
// do something with error
}];
]}
How to convert this to swift.
I am very new to swift
var url = NSString(string:"https://api.linkedin.com/v1/people/~")
if LISDKSessionManager.hasValidSession {
LISDKAPIHelper.sharedInstance().getRequest(url, success: {
response in
//Do something with the response
}, error: {
error in
//Do something with the error
})
}
This (I think its correct) is the translated version. I don't know Objective-C, I just used my knowledge of Swift to try and figure this out.
Have you learned about closures yet? If not, I don't recommend using SDKs like the LinkedIn one because they rely on closures for many networking requests. I'd check out Treehouse Inc., a coding course site which offers great courses on closures in Swift (along with a bunch of other stuff).
var url = "https://api.linkedin.com/v1/people/~"
if LISDKSessionManager.hasValidSession()
{
try? LISDKAPIHelper.sharedInstance().getRequest(url, success: {(_ response: LISDKAPIResponse) -> Void in
// do something with response
})
}
this is in swift 4

Authenticated http request swift Alamofire

I'm struggling with getting this to work to make request to my API. Without a token works, but when I try to add additional headers, things turn to be complicated, for me.
First, the structure.
one class called: APIAsyncTask that makes the requests
one class called APIParams, just a data holder to send parameters to the APIAsyncTask class.
one class called DatabaseAPI that makes that builds the parameters, and send that to the APIAsyncTask class.
DatabaseAPI
func someMethod()
{
let task = APIAsyncTasks()
task.registerCallback { (error, result) -> Void in
print("Finished task, back at DatabaseAPI")
}
let params2 = APIParams(request: .GET, apiPath: "Posts/1", apiToken: "4iTX-56w")
task.APIrequest(params2)
}
APIAsyncTask
This part is for fixing another error, because manager was not global, the task got cancelled quickly.
var manager : Manager!
init(authenticatedRequest : Bool, token: String?)
{
manager = Alamofire.Manager()
print("Pre \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
if(authenticatedRequest && token != nil)
{
var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders!
defaultHeaders["Authorization"] = "bearer \(token)"
let configuration = Manager.sharedInstance.session.configuration
configuration.HTTPAdditionalHeaders = defaultHeaders
manager = Alamofire.Manager(configuration: configuration)
}
print("Post \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
}
After some decision making, it comes down to this part.
private func GetRequest(url: String!,token : String?, completionHandler: (JSON?, NSURLRequest?, NSHTTPURLResponse?, NSError?) -> () ) -> ()
{
print("Begin Get Request")
if(token != nil)//if token is not nil, make authenticated request
{
print("just before request: \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
manager.request(.GET, url, parameters: nil, encoding: .JSON).responseJSON { (request, response, json, error) in
print("Get Request (authenticated), inside alamofire request")
var resultJson : JSON?
if(json != nil)
{
resultJson = JSON(json!)
}
completionHandler(resultJson, request, response, error)
}
}
else
{
//working part without token
So as the code is now, I get an error on completing:
Mattt himself gives the answer of using Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders
, so that should be fine...
I suspect it has something to do with the multiple threads, according to this blog. Or, since it is something about CFNetwork, it could be because my API does not use SSL? I disabled NSAppTransportSecurity
I'm kind of new to swift, so examples would be really appreciated! Thankyou!
So the majority of your code looks solid.
The error leads me to believe that CFNetwork is having difficulty figuring out how to compute the protection space for the challenge. I would also assume you are getting a basic auth challenge since you are attaching an Authorization header.
Digging through your logic a bit more with this in mind led me to see that your not attaching your token to the string properly inside the Authorization header. You need to do the following instead.
defaultHeaders["Authorization"] = "bearer \(token!)"
Otherwise your Authorization header value is going to include Optional(value) instead of just value.
That's the only issue I can see at the moment. If you could give that a try and comment back that would be great. I'll update my answer accordingly if that doesn't actually solve your problem.
Best of luck!
You can add your headers in your request with Alamofire 2 and Swift 2.
For an example: go to example

How do I create a test user for a native iOS app?

I'm trying to test my native iOS app and I've been having huge problems with test users. Basically it seems that the normal graph based way of creating test users doesn't work for native apps. When I try I get the following response from the server:
{
"error": {
"message": "(#15) This method is not supported for native apps",
"type": "OAuthException",
"code": 15
}
}
This seems to be supported by various posts on SO and a page on the old FB forums:
http://forum.developers.facebook.net/viewtopic.php?id=93086
People say that the only way to create test users for native apps is to create proper fake accounts on FB, which is against FB terms and conditions. Is this really my only option ? I can't believe the FB devs cannot support test accounts for native apps.
Anyone know of any legitimate way to create native app test users ?
At the top of the developer documentation for test users, a GUI for maintaining test users is also mentioned.
In addition to the Graph API functionality described below for managing test users programmatically, there is also a simple GUI in the Developer App, available on your app's Roles page as shown in the screenshots below. It exposes all of the API functions described in this document.
For some reason I don't understand, the Graph API calls aren't available if your app has an 'App Type' (under Settings -> Advanced) of Native/Desktop. But you can use the GUI instead.
You can create a new facebook test user for your app by calling the below function.
Please user your APP ID (go to this link to get your App's ID:https://developers.facebook.com/apps)
Please go to your app on facebook and get the App secret ID
-(void)createNewUser{
NSString *appID=#"Past your App ID"
NSString *appSecret=#"Past your App secret"
NSDictionary *params = #{
#"true": #"installed",
#"access_token": #"appID|appSecret",
};
NSString *path = [NSString stringWithFormat:#"/appID/accounts/test-users"];
/* make the API call */
[FBRequestConnection startWithGraphPath:path
parameters:params
HTTPMethod:#"POST"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
)
{
if (result && !error)
{
NSLog(#"Test-User created successfully: %#", result);
}
else
{
NSLog(#"Error creating test-user: %#", error);
NSLog(#"Result Error: %#", result);
}
}];
}
Please make sure you don't reveal your App secret ID to anyone and never include it in your app hardcoded like that.
Hope it helps
The following works for me using the Facebook iOS SDK v4.1.0:
// Listed as "App ID": https://developers.facebook.com/apps/
NSString *facebookAppId = #"1234_REPLACE_ME_5678";
// Listed as "App Token": https://developers.facebook.com/tools/accesstoken/
NSString *facebookAppToken = #"ABCD_REPLACE_ME_1234";
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:[NSString stringWithFormat:#"/%#/accounts/test-users",
facebookAppId]
parameters:#{#"installed" : #"true"}
tokenString:facebookAppToken
version:#"v2.3"
HTTPMethod:#"POST"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
NSDictionary *testUser,
NSError *facebookError) {
if (facebookError) {
NSLog(#"Error creating test user: %#", facebookError);
} else {
NSLog(#"access_token=%#", testUser[#"access_token"]);
NSLog(#"email=%#", testUser[#"email"]);
NSLog(#"user_id=%#", testUser[#"id"]);
NSLog(#"login_url=%#", testUser[#"login_url"]);
NSLog(#"password=%#", testUser[#"password"]);
}
}];
When successful, the output is then something like this:
access_token=CACQSQXq3KKYeahRightYouDontGetToLookAtMyAccessTokenspBkJJK16FHUPBTCKIauNO8wZDZD
email=aqhndli_huisen_1430877783#tfbnw.net
user_id=1384478845215781
login_url=https://developers.facebook.com/checkpoint/test-user-login/1384478845215781/
password=1644747383820
Swift4 version.
import FBSDKCoreKit
// App Token can be found here: https://developers.facebook.com/tools/accesstoken/
let appToken = "--EDITED--"
let appID = "--EDITED--"
let parameters = ["access_token": appID + "|" + appToken, "name": "Integration Test"]
let path = "/\(appID)/accounts/test-users"
guard let request = FBSDKGraphRequest(graphPath: path, parameters: parameters, httpMethod: "POST") else {
fatalError()
}
request.start { _, response, error in
if let error = error {
// Handle error.
return
}
if let response = response as? [AnyHashable: Any],
let userID = response["id"] as? String,
let token = response["access_token"] as? String,
let email = response["email"] as? String {
// Handle success response.
} else {
// Handle unexpected response.
}
}
You can use Facebook's Test User API https://developers.facebook.com/docs/test_users/ to create test users, just be sure to use the same App id.
Given a APP_ID and APP_SECRET, the following gives us an APP_ACCESS_TOKEN
curl -vv 'https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=APP_SECRET&grant_type=client_credentials'
then we can use the token to create test users:
curl 'https://graph.facebook.com/APP_ID/accounts/test-users?installed=true&name=NEW_USER_NAME&locale=en_US&permissions=read_stream&method=post&access_token=APP_ACCESS_TOKEN
in the response body to this command will the new users' email and password.

Resources