Azure Functions Script Compilation Failed - c#

I'm trying to create an Azure Function with Docker. When I create a Function with func new, it works fine and when I go to http://localhost:8182/api/HttpTriggerCSharp?name=John I get the message
Hello, John
Now I'm trying to run the same project but I changed the code. The previous code was this:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static IActionResult Run(HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = new StreamReader(req.Body).ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
Now this is my new code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static IActionResult Run(HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// Parsing query parameters
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
log.Info("name = " + name);
string numberOfTerms = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "numberOfTerms", true) == 0)
.Value;
log.Info("name = " + numberOfTerms);
// Validating the parameters received
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(numberOfTerms))
{
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"Please pass a name and the number of digits on the query string.");
return errorResponse;
}
int termsToShow;
try
{
termsToShow = Int32.Parse(numberOfTerms);
}
catch (FormatException e)
{
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"The numberOfTerms parameter must be an integer!");
return errorResponse;
}
if (termsToShow < 0 || termsToShow > 100) {
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"Please pass a numberOfTerms parameter between 0 and 100.");
return errorResponse;
}
// Building the response
string incompleteResponse = "Hello, " + name + ", you requested the first " + numberOfTerms + " terms of the Fibonacci series. Here they are: ";
string completeResponse = GenerateFibonacciTerms(incompleteResponse, termsToShow);
var response = req.CreateResponse(HttpStatusCode.OK, completeResponse);
// Returning the HTTP response with the string we created
log.Info("response = " + response);
return response;
}
public static string GenerateFibonacciTerms(string incompleteResponse, int termsToShow)
{
int a = 0;
int b = 1;
string temporalString = "";
for (int i = 0; i < termsToShow; i++)
{
int temp = a;
a = b;
b = temp + b;
temporalString = temporalString + temp.ToString() + " ";
}
string result = incompleteResponse + temporalString + "- That's it, have an awesome day!";
return result;
}
I build the container then I run it and I get this message:
I've already checked my code with VS Code (I did it in Sublime Text so I didn't have code checking) and all the problems it finds are the same error:
And my code has "errors" everywhere:
How can I solve this?

You are using v2 function core tools(based on .net core), while the code you changed is targeted at v1(.net framework).
So you have two choices:
Uninstall v2 and use function core tool v1.
Modify your code to make it work in v2 runtime.
Here the code for you to refer. GenerateFibonacciTerms method needs no change.
log.Info("C# HTTP trigger function processed a request.");
// Parsing query parameters
string name = req.Query["name"];
log.Info("name = " + name);
string numberOfTerms = req.Query["numberOfTerms"];
log.Info("numberOfTerms = " + numberOfTerms);
// Validating the parameters received
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(numberOfTerms))
{
return new BadRequestObjectResult("Please pass a name and the number of digits on the query string.");
}
int termsToShow;
try
{
termsToShow = Int32.Parse(numberOfTerms);
}
catch (FormatException e)
{
return new BadRequestObjectResult("The numberOfTerms parameter must be an integer!");
}
if (termsToShow < 0 || termsToShow > 100) {
return new BadRequestObjectResult("Please pass a numberOfTerms parameter between 0 and 100.");
}
// Building the response
string incompleteResponse = "Hello, " + name + ", you requested the first " + numberOfTerms + " terms of the Fibonacci series. Here they are: ";
string completeResponse = GenerateFibonacciTerms(incompleteResponse, termsToShow);
var response = new OkObjectResult(completeResponse);
// Returning the HTTP response with the string we created
log.Info("response = " + response);
return response;

Related

Why is my UpdateFile getting a null in the response in google drive api v3 using c#?

I have an application that is written in C# so that it can be a stand-alone .exe with parameters passed in via cmd at runtime. I am trying to update a file in google drive without changing the fileid so that any links using that id will not be broken. The following code works until it gets to the third section using Google.Apis.Drive.v3.Data.File updateFile in which the file is always 'null'. The request.ResponseBody has the correct fileId, the correct path and all but always comes up null.
How can I get this to update a file?
Is there some code update/change that no longer works?
Any help would be appreciated-
Section 1- This is the start of the code that calls the next two sections:
// System Library Requirements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security;
// Microsoft Library Requirements
using Microsoft.Win32;
// Google API Library Requirements
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using GoogleV2 = Google.Apis.Drive.v2;
using Google.Apis.Drive.v3.Data;
using GoogleV2data = Google.Apis.Drive.v2.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.Requests;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Api.Gax;
using Google.Cloud.Channel.V1;
// JSON reading/serializing/De-serializing Library Requirements
using Newtonsoft.Json;
// Mime-Type Library Requirements
using MimeKit;
using Google.Apis.Upload;
// CyberArk Library Requirements
using CyberArk.AIM.NetPasswordSDK;
using CyberArk.AIM.NetPasswordSDK.Exceptions;
static class Globals
{
//cyberark values will change based on the domain and the app and the safe
public static string google_client_secret;
public static string cyberark_client_id = "...xxxx.....apps.googleusercontent.com";
public static string cyberark_object = "...xxx...";
public static string cyberark_app_id = "...xxx...";
public static string cyberark_safe = "...xxx...";
//google app values will change based on the domain and gcp project information e.g. np vs prod
public static string google_app_name = "my_google_app_name";
public static string google_client_id = "my_google_client_id";
public static string google_data_store = "my_google_data_store";
}
class Program
{
static void Main(string[] args)
{
Globals.google_client_secret = NetPasswordSDK.CyberArkProgram.GetMyPassword(NetPasswordSDK.MyCredentials.MY_APP_ID, NetPasswordSDK.MyCredentials.MY_SAFE, NetPasswordSDK.MyCredentials.MY_OBJECT, NetPasswordSDK.MyCredentials.MY_SECURE_STRING);
var paramFolderID = "";
var paramDriveFromFolderName = "";
var paramUserID = "";
var paramTeamID = "";
var paramFileName = "";
var paramFilePath = "";
var paramLocalFolderPath = "";
if (args == null || args.Length == 0)
{
Console.WriteLine("No Arguments passed");
}
else
{
paramFolderID = args[0];
paramDriveFromFolderName = args[1];
paramUserID = args[2];
paramTeamID = args[3];
paramFileName = args[4];
paramFilePath = args[5];
paramLocalFolderPath = args[6];
Console.WriteLine("FolderID " + args[0]);
Console.WriteLine("DriveFromFolderName " + args[1]);
Console.WriteLine("UserID " + args[2]);
Console.WriteLine("TeamID " + args[3]);
Console.WriteLine("FileName " + args[4]);
Console.WriteLine("FilePath " + args[5]);
Console.WriteLine("LocalFolderPath " + args[6]);
}
string googleUpdateText = "";
string googleUpdateFilePath = "";
string googleUpdateLocalFolderPath = "";
string googleUpdateFileName = "";
string googleUpdateParentFolder = "";
string googleUpdateTeamDriveID = "";
string googleUpdateParentFolderID = "";
var proxy = WebRequest.DefaultWebProxy;
proxy.Credentials = CredentialCache.DefaultCredentials;
var httpClientHandler = new HttpClientHandler()
{
Proxy = proxy
};
{
{
//googleUpdateParentFolderID = paramFolderID;
//googleUpdateParentFolder = paramDriveFromFolderName;
//googleUpdateTeamDriveID = paramTeamID;
//googleUpdateFileName = paramFileName;
//googleUpdateFilePath = paramFilePath;
//googleUpdateLocalFolderPath = paramLocalFolderPath;
// HC values for testing
googleUpdateFilePath = "C:\\Path\\to_update_file\\update_file.txt";
googleUpdateLocalFolderPath = "C:\\Path\\to_update_file\\";
googleUpdateFileName = "update_file.txt";
googleUpdateParentFolder = "Folder_on_Google_Drive";
googleUpdateTeamDriveID = "Team_Drive_ID";
googleUpdateParentFolderID = "Parent_Folder_ID";
Console.WriteLine("value of googleUploadFilePath: " + googleUpdateFilePath);
Console.WriteLine("value of googleUploadLocalFolderPath: " + googleUpdateLocalFolderPath);
Console.WriteLine("value of googleUploadFileName: " + googleUpdateFileName);
Console.WriteLine("value of googleUploadParentFolder: " + googleUpdateParentFolder);
Console.WriteLine("value of googleUploadTeamDriveID: " + googleUpdateTeamDriveID);
Console.WriteLine("value of googleUploadParentFolderID: " + googleUpdateParentFolderID);
googleUpdateText = (JsonConvert.SerializeObject(GoogleAPI.GoogleAPI_Drive.GDrive_Update_File(googleUpdateFilePath, googleUpdateLocalFolderPath, googleUpdateFileName, "no description", googleUpdateParentFolder, paramUserID, "", "", googleUpdateTeamDriveID, googleUpdateParentFolderID), Formatting.Indented));
Console.WriteLine("value of googleUpdateText: " + googleUpdateText);
Console.WriteLine("this is the end of the update text");
};
// end
}
// end of Main
}
// end of program class
}
Section 2- This is the middle part of the code that updates files in a list of files which calls each file to update it.
public static String GDrive_Update_File(string pathToFile, string localFilePath, string fileName, string description, string parentFolder = "", string userid = "", string sharedUserIds = "", string sharerole = "", string teamID = "", string parentFolderID = "")
{
FileResponseJSON fileJson = new FileResponseJSON();
List<string> fileArray = new List<string> { };
IList<FileResponseJSON> filesJSON = new List<FileResponseJSON>();
IList<Google.Apis.Drive.v3.Data.File> files = new List<Google.Apis.Drive.v3.Data.File>();
List<string> parents = new List<String> { };
List<string> kids = new List<String> { };
UserCredential credential = GoogleAPI_Functions.GetCredentials(userid);
var service = new DriveService(new BaseClientService.Initializer
{
ApplicationName = APP_NAME,
HttpClientInitializer = credential,
});
if (!String.IsNullOrEmpty(parentFolder))
{
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.Q = String.Format("(name contains '{0}') and (mimeType = 'application/vnd.google-apps.folder')", parentFolder);
listRequest.Fields = "files(*)";
if (teamID.Length > 3)
{
listRequest.SupportsTeamDrives = true; // new
listRequest.Corpora = "teamDrive"; // new
listRequest.IncludeTeamDriveItems = true;//new
listRequest.TeamDriveId = teamID;//new
}
// List files.
IList<Google.Apis.Drive.v3.Data.File> folders = listRequest.Execute().Files;
foreach (var fldr in folders)
{
if (parentFolderID.Length > 3)
{
if (fldr.Id.Equals(parentFolderID))
{
parents.Add(fldr.Id);
}
}
else
{
parents.Add(fldr.Id);
}
Console.WriteLine("value of fldr.Id " + fldr.Id);
}
}
// Upload File MetaData
// mimeType is important and so it the 'trashed' true/false or it won't be found
FilesResource.ListRequest fileRequest = service.Files.List();
string query = "name contains 'update_file.txt' and (mimeType = 'text/plain') and trashed = false";
fileRequest.Q = String.Format(query);
fileRequest.Fields = "files(*)";
fileRequest.SupportsTeamDrives = true; // new
fileRequest.Corpora = "teamDrive"; // new
fileRequest.IncludeTeamDriveItems = true;//new
fileRequest.TeamDriveId = teamID;//new
IList<Google.Apis.Drive.v3.Data.File> myFiles = fileRequest.Execute().Files;
Console.WriteLine("Files:");
if (myFiles != null && myFiles.Count > 0)
{
foreach (var myf in myFiles)
{
myf.Name = localFilePath + myf.Name;
Console.WriteLine("{0} ({1})", myf.Name, myf.Id);
UpdateFile(service, myf.Name, myf.Id);
}
}
else
{
Console.WriteLine("No files found.");
}
Console.Read();
var UpdatedID = "";
return UpdatedID;
}
Section 3- This section is supposed to update each file --> THIS IS THE SECTION WITH THE PROBLEM (The Google.Apis.Drive.v3.Data.File updatedFile always returns null)
public static Google.Apis.Drive.v3.Data.File UpdateFile(DriveService _service, string _uploadFile, string _fileId)
{
Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
byte[] byteArray = System.IO.File.ReadAllBytes(_uploadFile);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
FilesResource.UpdateMediaUpload request = _service.Files.Update(file, _fileId, stream, GetMimeType(_uploadFile));
void Upload_ProgressChanged(IUploadProgress progress) =>
Console.WriteLine(progress.Status + " " + progress.BytesSent);
void Upload_ResponseReceived(Google.Apis.Drive.v3.Data.File myfile) =>
Console.WriteLine(myfile.Name + " was uploaded successfully");
request.ProgressChanged += Upload_ProgressChanged;
request.ResponseReceived += Upload_ResponseReceived;
request.Upload();
Google.Apis.Drive.v3.Data.File updatedFile = request.ResponseBody;
return updatedFile;
}
update: I used IUploadProgress.Exception and got the following error-
Exception: The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
File not found: 1XYZ..... [404]
Errors [
Message[File not found: 1XYZ.......] Location[fileId - parameter] Reason[notFound] Domain[global]
]
at Google.Apis.Upload.ResumableUpload.<HandleResponse>d__78.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<SendNextChunkAsync>d__77.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<UploadCoreAsync>d__74.MoveNext() 0
Update again, I was able to see the uri that was being generated and copy/paste it into the browser, and when I did I got a 405 Method Not Allowed; additionally when I tried it without the https and just used http I got a 403 Forbidden and that SSL is required to perform this operation
This is the uri without the ids
https://www.googleapis.com/upload/drive/v3/files/[file_id]?uploadType=resumable&upload_id=[upload_id]
Update:
During the process the api produces these two urls that both work and allow me access
WebContentLink
"https://drive.google.com/uc?id=[docID]&export=download"
WebViewLink
"https://drive.google.com/file/d/[docID]/view?usp=drivesdk"
Latest update applied fixes from DalmTo but am getting new errors-
// - removed // Google.Apis.Drive.v3.Data.File file = new
// - added //
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Id = _fileId
};
var fsSource = new MemoryStream(Encoding.UTF8.GetBytes(_uploadFile ?? ""));
var request = _service.Files.Update(fileMetadata, fileMetadata.Id, fsSource, GetMimeType(_uploadFile));
New error:
Exception: The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
The resource body includes fields which are not directly writable. [403]
Errors [
Message[The resource body includes fields which are not directly writable.] Location[ - ] Reason[fieldNotWritable] Domain[global]
]
Not sure what would cause this.
File id is writeable so you should be able to send it with the file metadata in your request.
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Id = "xxx"
};
var fsSource = new MemoryStream(Encoding.UTF8.GetBytes(uploadString ?? ""));
var request = service.Files.Update(fileMetadata, fileMetadata.Id , fsSource, GetMimeType(uploadFilePath));
Update
From comments you are sending an empty metadata file object with your request.
Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
There for Drive is always going to give you a new file id. I am sending the original file id as part of the metadata there for it will use the file id.

C# Console application wont post large dataset to asp.net .net core 3.0 web api

Im strugling with my last step of my console application. I have a post function which posts a car brand including models, generations, modifications and car data. As you can imagine thats a large dataset. Smaller brands are send no problem but when i come to bigger brands like audi , bmw and mercedes then my application doesnt post.
Here is the Console application code who sends the data
private static async Task PostDataSet(List<Brand> DataSet)
{
string key = Api_Key;
if (key == null || key == "")
{
Console.WriteLine("No key set");
Console.ReadKey();
return;
}
Console.WriteLine("Posting a batch with " + DataSet.Count + " items :" + DataSet[0].Name);
try
{
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["Key"] = Api_Key;
data["Dataset"] = JsonConvert.SerializeObject(DataSet).ToString();
data["HostIp"] = Hostip;
string ResponseString = "";
if (UseLive)
{
Uri x = new Uri(LiveUrl + "/Api/FullApiUpdate");
var response = await wb.UploadValuesTaskAsync(x, "POST", data);
ResponseString = Encoding.UTF8.GetString(response);
Console.WriteLine(ResponseString);
}
if (!UseLive)
{
Uri x = new Uri(localurl + "/Api/FullApiUpdate");
var response = await wb.UploadValuesTaskAsync(x, "POST", data);
ResponseString = Encoding.UTF8.GetString(response);
Console.WriteLine(ResponseString);
}
if (ResponseString == "No api key specified" || ResponseString == "Invalid Api Key" || ResponseString == "Received null")
{
Console.WriteLine("Api posted a cancellation token : " + ResponseString);
Console.ReadKey();
}
}
}
catch (Exception e)
{
Console.WriteLine("Error posting to api");
Console.WriteLine(e.InnerException);
}
}
Everything works for smaller brands but like i told you earlier big brands break. The Server side api action looks like this
public async Task<string> FullApiUpdate(string Dataset , string HostIp, string Key)
{
Console.WriteLine("API KEY ACCESED THIS ACTION : " + Key);
Console.WriteLine("IP ACCESED THIS ACTION : " + HostIp);
if (Key == null || Key == "")
{
return "No api key specified";
}
if (await _context.Api_Keys.Where(x=>x.Key == Key && x.Active == true).FirstOrDefaultAsync()==null)
{
return "Invalid Api Key";
}
if (Dataset != null || Dataset.Count() != 0)
{
List<Brand> FullSet = JsonConvert.DeserializeObject<List<Brand>>(Dataset);
if (FullSet.Count() != 0)
{
Console.WriteLine("Reveived a batch of " + FullSet.Count() + " items from : " + HostIp);
await _context.AddRangeAsync(FullSet);
await _context.SaveChangesAsync();
return "Updated a batch of " + FullSet.Count() + " Items";
}
}
return "Received null";
}
I dont really know where to go from here Here are the console outputs which i have.
Server :
Server
Console :
Console
i have tried this in my startup
services.Configure<FormOptions>(x =>
{
x.BufferBody = false;
x.KeyLengthLimit = 2048; // 2 KiB
x.ValueLengthLimit = 4194304; // 32 MiB
x.ValueCountLimit = 2048;// 1024
x.MultipartHeadersCountLimit = 32; // 16
x.MultipartHeadersLengthLimit = 32768; // 16384
x.MultipartBoundaryLengthLimit = 256; // 128
x.MultipartBodyLengthLimit = 134217728; // 128 MiB
});
I have searched multiple forums but none of them worked for me. Any help is welcome
I fixed my problem by increasing the size to int.maxvalue
services.Configure<FormOptions>(x =>
{
x.BufferBody = false;
x.KeyLengthLimit = int.MaxValue; // 2 KiB
x.ValueLengthLimit = int.MaxValue; // 32 MiB
x.ValueCountLimit = int.MaxValue;// 1024
x.MultipartHeadersCountLimit = int.MaxValue; // 16
x.MultipartHeadersLengthLimit = int.MaxValue; // 16384
x.MultipartBoundaryLengthLimit = int.MaxValue; // 128
x.MultipartBodyLengthLimit = int.MaxValue; // 128 MiB
});

How to access a specific Post parameter in an HTTP Post WebHook?

I'm using JotForms to send a POST Message using the WebHook integration. This is the message that is sent.
RequestBin Message
I'm implementing a WebService in C# using Azure Functions in order to Insert the values from the form to an SQL-Server.
#r "System.Data"
using System.Net;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string name = "";
dynamic body = await req.Content.ReadAsStringAsync();
log.Info(body);
var e = JsonConvert.DeserializeObject<Person>(body as string);
name = e.firstname + " " + e.lastname;
//Connect to SQL
var cnnString = "Server=tcp:XXXX.database.windows.net,1433;"+"Initial Catalog=XXXX;"
+"Persist Security Info=False;"+"User ID=XXX;"+"Password=XXXX;"+"MultipleActiveResultSets=False;"
+"Encrypt=True;"+"TrustServerCertificate=False;"+"Connection Timeout=30;";
using (SqlConnection conn = new SqlConnection(cnnString))
{
conn.Open();
// Insert Signup
var signupInsert = "INSERT INTO [dbo].[test_data] ([firstname],[lastname],[date])" +
"VALUES ('" + e.q8_FirstName + "','" + e.q9_yTus + "','" + e.q24_Birthday + "')";
// Execute and load data into database.
using (SqlCommand cmd = new SqlCommand(signupInsert, conn))
{
var rows = cmd.ExecuteNonQuery();
}
return name == " "
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Ok" );
}
}
public class Person{
public string firstname {get;set;}
public string lastname {get;set;}
public string date {get;set;}
public string q8_FirstName {get;set;}
public string q9_yTus {get;set;}
public string q24_Birthday {get;set;}
}
I've managed to insert sucessfully when I test with a body that is just the JSON
{
"firstname": "ni\u00f1o",
"lastname": "Lachner2",
"date":"08/08/1994",
"name":"Vincenz2",
"slug":"submit\/80565829893878\/",
"input_language":"Espa\u00f1ol",
"q8_FirstName":"Nombre",
"q9_yTus":"Apellido",
"q24_Birthday":"08\/08\/1994",
"q5_cedula":"115850853",
"q18_country":"Costa Rica",
"dropdown_search":"",
"q3_province":"San Jos\u00e9",
"q11_cantonSanJose":"Alajuelita",
"q12_cantonAlajuela":"",
"q13_cantonHeredia":"",
"q14_cantonCartago":"",
"q15_cantonPuntarenas":"",
"q16_cantonPuntarenas":"",
"q17_cantonGuanacaste":"",
"q6_phone":"88141833",
"q2_mail":"vincenz.lachner#gmail.com",
"q7_shirtSize":"S",
"q25_channel":{"0":"Correo electr\u00f3nico","other":"YOU"},
"q27_politicaDe":"Accepted",
"preview":"true"
}
How can i access the JSON in the rawRequest? That is in the RawBody under the name rawRequest.
and what are these separators? --------------------------e5d83c25c3d6dcc0
--------------------------e5d83c25c3d6dcc0
Content-Disposition: form-data; name="rawRequest"
{"slug":"submit\/80565829893878\/","input_language":"Espa\u00f1ol","q8_FirstName":"textbox_sample0","q9_yTus":"textbox_sample1","q24_Birthday":"11\/11\/1111","q5_cedula":"1","q18_country":"Costa Rica","dropdown_search":"","q3_province":"San Jos\u00e9","q11_cantonSanJose":"Alajuelita","q12_cantonAlajuela":"","q13_cantonHeredia":"","q14_cantonCartago":"","q15_cantonPuntarenas":"","q16_cantonPuntarenas":"","q17_cantonGuanacaste":"","q6_phone":"1","q2_mail":"john#example.com","q7_shirtSize":"XS","q25_channel":["Facebook"],"q27_politicaDe":"Accepted","preview":"true"}
It should be something like
var form = await req.Content.ReadAsFormDataAsync();
var body = form["rawRequest"];
If you send the message from JotForms, You could use the following code to get the
rawRequest or you could get pretty property value that is the fields in the JotForms.
The following is my detail steps:
1.Create the JotForms
2.Add the follow code to get the rawRequest
if (req.Content.IsMimeMultipartContent())
{
var content = await req.Content.ReadAsMultipartAsync();
var test = content.Contents.ToList();
Dictionary<string, string> dic = new Dictionary<string, string>();
foreach (var item in test)
{
var value = await item.ReadAsStringAsync();
dic.Add(item.Headers.ContentDisposition.Name, value);
log.Info(value);
}
foreach (var item in dic)
{
log.Info($"{item.Key}:{item.Value}");
}
}
3. Remote debug on my side.

Null Reference Exception From Azure Notification Hubs

I have been using Azure Notification Hubs along with GCM to send notifications to the users of my app. This was all working great until I published the app to the Play Store.
Now it gives a 500 Server error whenever I try to post a notification. I have no idea why this error is happening. Perhaps the app is not picking up the RavenDB where the notifications are stored?
But it looks more like the service is not getting back users that are registered on the Hub. I really don't know... Any help would be so appreciated!
This is my stacktrace when run locally, it is the same but less detailed when published:
"Message": "An error has occurred.",
"ExceptionMessage": "Value cannot be null.\r\nParameter name: source",
"ExceptionType": "System.ArgumentNullException",
"StackTrace": " at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)\r\n
at AcademicAssistantService.Controllers.NotificationController.<GetRecipientNamesFromNotificationHub>d__8.MoveNext()
in C:\\Users\\Kenneth\\Documents\\College\\Semester 8\\AcademicAssistantService\\AcademicAssistantService\\Controllers\\NotificationController.cs:line 105
This is the Controller action:
// POST api/notification
public async Task<IHttpActionResult> Post([FromBody]Notification notification, String key)
{
var notificationToSave = new Notification
{
NotificationGuid = Guid.NewGuid().ToString(),
TimeStamp = DateTime.UtcNow,
Message = notification.Message,
SenderName = notification.SenderName
};
var recipientNames = await GetRecipientNamesFromNotificationHub(key);
var recipientNamesString = CreateCustomRecipientNamesString(recipientNames);
string notificationJsonPayload =
"{\"data\" : " +
" {" +
" \"message\": \"" + notificationToSave.Message + "\"," +
" \"senderName\": \"" + notificationToSave.SenderName + "\"," +
" \"recipientNames\": \"" + recipientNamesString + "\"" +
" }" +
"}";
if (key == null)
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
else
{
foreach (string r in recipientNames)
{
if ((r != notification.SenderName))
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload, "user:" + r);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
}
}
await Session.StoreAsync(notificationToSave);
return Ok(notificationToSave);
}
To get names from hub:
public async Task<List<string>> GetRecipientNamesFromNotificationHub(String key)
{
var registrationDescriptions = await _hubClient.GetAllRegistrationsAsync(Int32.MaxValue);
var recipientNames = new List<String>();
foreach (var registration in registrationDescriptions)
{
if (registration is GcmRegistrationDescription)
{
var userName = registration.Tags
.Where(t => t.StartsWith("user"))
.Select(t => t.Split(':')[1].Replace("_", " "))
.FirstOrDefault();
userName = userName ?? "Unknown User";
Conversation convo = db.Conversations.Find(key);
foreach (User u in convo.Users)
{
if (u.Email == userName && !recipientNames.Contains(userName))
{
recipientNames.Add(userName);
}
}
}
}
return recipientNames;
}
Could you use Service Bus Explorer and verify indeed you have tags starts with "user". And I also see you are using GetAllRegistrationsAsync API, which is recommend to use only for debugging purpose. This is heavily throttled API.
Thanks,
Sateesh

C# Web API method returns 403 Forbidden

Solved!!! - See last edit.
In my MVC app I make calls out to a Web API service with HMAC Authentication Filterign. My Get (GetMultipleItemsRequest) works, but my Post does not. If I turn off HMAC authentication filtering all of them work. I'm not sure why the POSTS do not work, but the GETs do.
I make the GET call from my code like this (this one works):
var productsClient = new RestClient<Role>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
var getManyResult = productsClient.GetMultipleItemsRequest("api/Role").Result;
I make the POST call from my code like this (this one only works when I turn off HMAC):
private RestClient<Profile> profileClient = new RestClient<Profile>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
[HttpPost]
public ActionResult ProfileImport(IEnumerable<HttpPostedFileBase> files)
{
//...
var postResult = profileClient.PostRequest("api/Profile", newProfile).Result;
}
My RestClient builds like this:
public class RestClient<T> where T : class
{
//...
private void SetupClient(HttpClient client, string methodName, string apiUrl, T content = null)
{
const string secretTokenName = "SecretToken";
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (_hmacSecret)
{
client.DefaultRequestHeaders.Date = DateTime.UtcNow;
var datePart = client.DefaultRequestHeaders.Date.Value.UtcDateTime.ToString(CultureInfo.InvariantCulture);
var fullUri = _baseAddress + apiUrl;
var contentMD5 = "";
if (content != null)
{
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json); // <--- Javascript serialized version is hashed
}
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
var hmac = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
client.DefaultRequestHeaders.Add(secretTokenName, hmac);
}
else if (!string.IsNullOrWhiteSpace(_sharedSecretName))
{
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
client.DefaultRequestHeaders.Add(secretTokenName, sharedSecretValue);
}
}
public async Task<T[]> GetMultipleItemsRequest(string apiUrl)
{
T[] result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "GET", apiUrl);
var response = await client.GetAsync(apiUrl).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T[]>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
public async Task<T> PostRequest(string apiUrl, T postObject)
{
T result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "POST", apiUrl, postObject);
var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false); //<--- not javascript formatted
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
//...
}
My Web API Controller is defined like this:
[SecretAuthenticationFilter(SharedSecretName = "xxxxxxxxxxxxxxx", HmacSecret = true)]
public class ProfileController : ApiController
{
[HttpPost]
[ResponseType(typeof(Profile))]
public IHttpActionResult PostProfile(Profile Profile)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
GuidValue = Guid.NewGuid();
Resource res = new Resource();
res.ResourceId = GuidValue;
var data23 = Resourceservices.Insert(res);
Profile.ProfileId = data23.ResourceId;
_profileservices.Insert(Profile);
return CreatedAtRoute("DefaultApi", new { id = Profile.ProfileId }, Profile);
}
}
Here is some of what SecretAuthenticationFilter does:
//now try to read the content as string
string content = actionContext.Request.Content.ReadAsStringAsync().Result;
var contentMD5 = content == "" ? "" : Hashing.GetHashMD5OfString(content); //<-- Hashing the non-JavaScriptSerialized
var datePart = "";
var requestDate = DateTime.Now.AddDays(-2);
if (actionContext.Request.Headers.Date != null)
{
requestDate = actionContext.Request.Headers.Date.Value.UtcDateTime;
datePart = requestDate.ToString(CultureInfo.InvariantCulture);
}
var methodName = actionContext.Request.Method.Method;
var fullUri = actionContext.Request.RequestUri.ToString();
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var expectedValue = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
// Are the hmacs the same, and have we received it within +/- 5 mins (sending and
// receiving servers may not have exactly the same time)
if (messageSecretValue == expectedValue
&& requestDate > DateTime.UtcNow.AddMinutes(-5)
&& requestDate < DateTime.UtcNow.AddMinutes(5))
goodRequest = true;
Any idea why HMAC doesn't work for the POST?
EDIT:
When SecretAuthenticationFilter tries to compare the HMAC sent, with what it thinks the HMAC should be they don't match. The reason is the MD5Hash of the content doesn't match the MD5Hash of the received content. The RestClient hashes the content using a JavaScriptSerializer.Serialized version of the content, but then the PostRequest passes the object as JsonMediaTypeFormatted.
These two types don't get formatted the same. For instance, the JavaScriptSerializer give's us dates like this:
\"EnteredDate\":\"\/Date(1434642998639)\/\"
The passed content has dates like this:
\"EnteredDate\":\"2015-06-18T11:56:38.6390407-04:00\"
I guess I need the hash to use the same data that's passed, so the Filter on the other end can confirm it correctly. Thoughts?
EDIT:
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the sent content (formatted via JSON) will match the hashed content.
I was not the person who wrote this code originally. :)
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the content used for the hash will be formatted as JSON and will match the sent content (which is also formatted via JSON).

Categories

Resources