HttpClient pass multiple simple parameters - asp.net-mvc

I'm trying to do a POST from one controller to another controller. Both controller's are from different projects. One project is serving to simulate the presentation layer (which I will call the test project here).
From the test project I'm trying to pass 2 simple string parameters to the other controller which I will call the process.
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("id", param.Id.Value));
values.Add(new KeyValuePair<string, string>("type", param.Type.Value));
var content = new FormUrlEncodedContent(values);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("nl-NL"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string token = param.token.Value;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = client.PostAsync("/api/Process/Product", content).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
return Request.CreateResponse(HttpStatusCode.OK, result);
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "fail");
}
And in the process controller, I'm trying to receive it like this:
[HttpPost]
public HttpResponseMessage Product(string id, string type)
{
return null;
}
But it never reaches this controller. I always get a "not found status code".
So how can I pass 2 simple parameters with HttpClient()?

Use Get instead of Post for simple type parameters.
using (var client = new HttpClient())
{
BaseAddress = new Uri(url);
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("nl-NL"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string token = param.token.Value;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// New code:
var response = await client.GetAsync( string.format("api/products/id={0}&type={1}",param.Id.Value,param.Id.Type) ).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
return Request.CreateResponse(HttpStatusCode.OK, result);
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "fail");
}
In the API side you can do like this.
[HttpGet]
public HttpResponseMessage Product(string id, string type)
{
return null;
}

When receiving a post you must specify [FromBody] in the parameters for the method to be called
[HttpPost]
public HttpResponseMessage Product([FromBody]string id, [FromBody]string type)
{
return null;
}

I'm not sure I am totally in love with it, but I used anonymous types and dynamics to handle this in the past... (Note the config differences for using PostAsJsonAsync(). I forgot these initially.)
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.PostAsJsonAsync("api/User/UpdateLastLogin", new { UserId = userId, ApplicationId = applicationId });
Receiving controller:
[HttpPost]
public void UpdateLastLogin([FromBody]dynamic model)
{
_userRepository.UpdateLastLogin((int)model.UserId, (int)model.ApplicationId);
}
In WebApiConfig.Register():
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
The other approach, which I passed on, is to create a completely new set of strongly types models for every one of these calls. I didn't want to, as I had a ton of them to make to a WebAPI.

Here is another example you can use for WinForms, WPF or Console apps
Client code
async Task<CalendarView> GetData(int month, int year, int deviceTypeID)
{
var result = new MSOCommon.CalendarView();
try
{
HttpClient client = new HttpClient();
var calendarRequest = new CalendarRequest()
{
Month = month,
Year = year,
DeviceTypeID = deviceTypeID,
UserInfo = Program.UserInfo
};
var url = Properties.Settings.Default.ServerBaseUrl + string.Format("/api/calendar/Calendar");
HttpResponseMessage response = await client.PostAsync(url, calendarRequest.AsJson());
if (response.IsSuccessStatusCode) // Check the response StatusCode
{
var serSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
string responseBody = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<MSOCommon.CalendarView>(responseBody, serSettings);
}
else
{
logger.Error(Properties.Resources.DATACannotGetCalendar);
}
}
catch (Exception ex)
{
logger.Error(Properties.Resources.DATACannotGetCalendar + " " + ex.Message);
logger.Error(ex);
}
return result;
}
Controller server-side code
[HttpPost()]
public CalendarView Calendar(CalendarRequest calendarRequest)
{
logger.Info(string.Format("Get calendar for month {0} and year {1} ", calendarRequest.Month, calendarRequest.Year));
// TODO Check username
var result = new CalendarView();
using (var db = new MSOnlineEntities())
{
result = db.Calendars.Include("CalendarDetails")
.Where(x => x.CMonth == calendarRequest.Month && x.CYear == calendarRequest.Year && x.CDeviceTypeID == calendarRequest.DeviceTypeID).ToList()
.ConvertAll(x => new CalendarView
{
ID = x.ID,
CMonth = x.CMonth,
CYear = x.CYear,
CDays = x.CDays,
CDeviceTypeID = x.CDeviceTypeID,
ClosedAtTime = x.ClosedAtTime,
ClosedByUser = x.ClosedByUser,
IsClosed = x.IsClosed,
CalendarDetails = x.CalendarDetails.ToList().ConvertAll(d => new CalendarDetailView
{
ID = d.ID,
CalendarID = d.CalendarID,
MachineID = d.MachineID,
MachineName = d.DATA_MACHINE.Name,
D1 = d.D1 ?? -1,
D2 = d.D2 ?? -1,
D3 = d.D3 ?? -1,
D4 = d.D4 ?? -1,
D5 = d.D5 ?? -1,
D6 = d.D6 ?? -1,
D7 = d.D7 ?? -1,
D8 = d.D8 ?? -1,
D9 = d.D9 ?? -1,
D10 = d.D10 ?? -1,
D11 = d.D11 ?? -1,
D12 = d.D12 ?? -1,
D13 = d.D13 ?? -1,
D14 = d.D14 ?? -1,
D15 = d.D15 ?? -1,
D16 = d.D16 ?? -1,
D17 = d.D17 ?? -1,
D18 = d.D18 ?? -1,
D19 = d.D19 ?? -1,
D20 = d.D20 ?? -1,
D21 = d.D21 ?? -1,
D22 = d.D22 ?? -1,
D23 = d.D23 ?? -1,
D24 = d.D24 ?? -1,
D25 = d.D25 ?? -1,
D26 = d.D26 ?? -1,
D27 = d.D27 ?? -1,
D28 = d.D28 ?? -1,
D29 = d.D29 ?? -1,
D30 = d.D30 ?? -1,
D31 = d.D31 ?? -1
})
}).FirstOrDefault();
return result;
}
}

Use [HttpGet] instead of [HttpPost] on the server
HttpResponseMessage response =
await client.GetAsync(string.format("api/product?id={0}&type={1}",param.Id.Value,param.Id.Type);
The difference between my answer and the NMK's accepted answer is the ? instead of the /
This is what worked for me

Related

Bad request when posting to OData Data Entity in Dynamics 365

I've created a public Data Entity in dynamics with the following fields:
I keep getting a bad request response, but I'm not sure why.
I've tried to make a POST request in two ways:
1.
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionStatus = "Created" };
string jsonMessage = JsonConvert.SerializeObject(hireAction);
using (HttpClient client = new HttpClient())
{
HttpRequestMessage requestMessage = new
HttpRequestMessage(HttpMethod.Post, "MyDynamicsEnvironmentName/data/HireActions?cross-company=true");
requestMessage.Content = new StringContent(jsonMessage, Encoding.UTF8, "application/json");
requestMessage.Headers.Add("Authorization", AuthResult.AuthorizationHeader);
HttpResponseMessage response = client.SendAsync(requestMessage).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
//Logic
}
}
var url = "MyDynamicsEnvironmentName/data/HireActions?cross-company=true";
var req = HttpWebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/json";
req.Headers["Authorization"] = AuthResult.AuthorizationHeader;
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionId = "12345", HireActionStatus = "Created" };
var jsonSettings = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Local
};
var postString = "CompanyName='DEMF'" + "&MovieId='DEMF-000000014'" + "&HireActionId=132&HireActionStatus='Created'";
var data = JsonConvert.SerializeObject(postString, jsonSettings);
var bytes = Encoding.Default.GetBytes(postString);
var newStream = req.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
using (var resp = req.GetResponse())
{
var results = new StreamReader(resp.GetResponseStream()).ReadToEnd();
}
Some keypoints:
-Of course you'd replace MyDynamicsEnvironmentName with the URL for the environment. The URL is correct and verified however, by the fact that GET requests do work
-The Authresult.AuthorizationHeader contains a valid token, also validated by working GET requests
As said before, both of these result in a bad request. Does someone know what is wrong or missing?

SharePoint Helper Class: WebRequest to HttpClient

I have a scenario where I need to use an OnPrem-SharePoint for File Upload/Download. It's SharePoint 2016 and I found a helper class from the old days that uses WebRequests to communicate. I must use the same class in my .net 7 Minimal API project. It works but I don't like the tech debt I have to carry by using WebRequests. There are quite a few methods there but I need help converting these main ones and I can manage the rest as they are somewhat copy-paste:
public partial class SharePointService : ISharePointService
{
private const string AuthUrl = "https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2";
private readonly SPAppInit spAppInit;
public SharePointService(SPAppInit spAppInit) => this.spAppInit = spAppInit;
public SPAuthToken? GetAuthToken()
{
var postParameters = new[]
{
new KeyValuePair<string, string>("grant_type", spAppInit.GrantType),
new KeyValuePair<string, string>("client_id", $"{spAppInit.AppClientId}#{spAppInit.TenantId}"),
new KeyValuePair<string, string>("client_secret", spAppInit.AppSecret),
new KeyValuePair<string, string>("resource", $"00000000-0000-0aaa-ce00-000000000000/{spAppInit.TenantName}#{spAppInit.TenantId}"),
};
var postData = postParameters.Aggregate("", (current, t) => current + HttpUtility.UrlEncode(t.Key) + "=" + HttpUtility.UrlEncode(t.Value) + "&");
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(string.Format(AuthUrl, spAppInit.TenantId));
myHttpWebRequest.Method = "POST";
var data = Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
var requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
var myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
var responseStream = myHttpWebResponse.GetResponseStream();
var myStreamReader = new StreamReader(responseStream, Encoding.Default);
var result = myStreamReader.ReadToEnd();
var authToken = JsonSerializer.Deserialize<SPAuthToken>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return authToken;
}
public string GetRequestDigest(string authToken)
{
const string endpoint = "/_api/contextInfo";
var request = (HttpWebRequest)WebRequest.Create(spAppInit.SPSiteUrl + endpoint);
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
//request.Accept = "application/json;odata=verbose";
request.ContentLength = 0;
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
// parse the ContextInfo response
var resultXml = XDocument.Parse(result);
// get the form digest value
var e = from r in resultXml.Descendants()
where r.Name == XName.Get("FormDigestValue", "http://schemas.microsoft.com/ado/2007/08/dataservices")
select r;
return e.First().Value;
}
public string CreateFolder(string authToken, string requestDigest, string folderName)
{
var request = (HttpWebRequest)WebRequest.Create($"{spAppInit.SPSiteUrl}/_api/web/getfolderbyserverrelativeurl('{spAppInit.SPDocLibServerRelativeUrl}/')/folders");
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
request.Headers["X-RequestDigest"] = requestDigest;
request.Accept = "application/json;";
request.ContentType = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
var requestParams = CreateRequest(folderName);
var json = JsonSerializer.Serialize(requestParams);
streamWriter.Write(json);
}
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
var folderCreationResponse = JsonSerializer.Deserialize<SPFolderCreationResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return folderCreationResponse?.ServerRelativeUrl ?? string.Empty;
}
public void UploadLargeFile(string authToken, string formDigest, string sourcePath, string targetFolderUrl, int chunkSizeBytes = 2048)
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var fileName = Path.GetFileName(sourcePath);
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = File.OpenRead(sourcePath);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
public void UploadLargeFileUsingBytes(string authToken, string formDigest, string fileName, byte[] fileBytes, string targetFolderUrl, int chunkSizeBytes = 2048)
{
if (fileBytes.Length < chunkSizeBytes)
{
UploadSmallFile(authToken, formDigest, targetFolderUrl, fileName, fileBytes);
}
else
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = ReadFileStreamFromByte(fileBytes);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk && inputStream.Length > chunkSizeBytes)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
}
}
I don't like pasting the whole project. However, even after being very selective, this is still a lot of code, but its communication code that I do not understand as I started with the newer HttpClient only as (some random HttpClient injection in my API):
services.AddCustomHttpClient<ILetterServiceApi, LetterServiceApi>(Constants.ApiServiceNamedClientLetter, baseAddress!);
Where AddCustomHttpClient is,
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomHttpClient<TInterface, TImplementation>(this IServiceCollection services, string serviceName, string baseAddress)
where TInterface: class
where TImplementation: class, TInterface
{
services.AddHttpClient<TInterface, TImplementation>(serviceName, config =>
{
config.BaseAddress = new Uri(baseAddress);
config.Timeout = new TimeSpan(0, 0, 30);
})
.AddHttpMessageHandler<RequestHandler>();
return services;
}
}
I have made a lot of modern modifications to the code and also Am in the process of converting this class (in a remote assembly) to conform to the .Net Core's .AddSharePoint() DI pattern, and for that, I need to get rid of obsolete (HttpWebRequest)WebRequest
Update:
I managed to convert a rather simple method:
private SPFolderResponse? GetFolderFiles(string authToken, string requestDigest, string folderName)
{
using var client = new HttpClient();
var serverRelativeFolderUrl = $"{spAppInit.RelativeUrl}/{folderName}";
var folderApiUrl = $"{spAppInit.SiteUrl}/_api/web/GetFolderByServerRelativeUrl('/{serverRelativeFolderUrl}')/Files";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-RequestDigest", requestDigest);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {authToken}");
var result = client.GetStringAsync(folderApiUrl).GetAwaiter().GetResult();
var response = JsonSerializer.Deserialize<SPFolderResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return response;
}
But the other ones like the Token, Digest and Upload ones are still dodgy...

Cannot refresh listview with observablecollection

I am trying to refresh my listview when an item is removed from it, but every time it gives me this error:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: index.
Before updating the ObservableCollection, I do this:
Groups = new ObservableCollection<RequestGroups>();
And then I fill it with this:
var temp = (JArray)resultJson["data"];
JArray jarr = temp;
foreach (JObject contents in jarr.Children<JObject>())
{
Requests obj = new Requests();
obj.Id = (int)contents["id"];
Client c = new Client();
c.address = contents["address"].ToString();
c.phone = contents["phone"].ToString();
c.name = contents["user"].ToString();
obj.Client = c;
obj.Date = contents["date"].ToString();
obj.Duration = contents["duration"].ToString();
obj.DurationText = "Duración: "+contents["duration"].ToString()+"h";
obj.Price = "$" + contents["price"].ToString();
String[] cDate = obj.Date.Split(' ');
String cHour = cDate[1]+" "+cDate[2];
obj.Hour = cHour;
String[] date = cDate[0].Split('-');
String title = months[date[1]] + " " + date[0];
obj.Title = title;
bool flag = false;
foreach(RequestGroups rqG in Groups){
if(rqG.Title.Equals(title)){
rqG.Add(obj);
flag = true;
}
}
if(!flag){
RequestGroups rq = new RequestGroups(title, date[1] + "-" + date[0]);
rq.Add(obj);
Device.BeginInvokeOnMainThread(() =>
{
Groups.Add(rq);
});
}
}
This is where I remove the items:
private async Task UpdateRequest(int status,int idEvent)
{
HttpClient hTTPClient = new HttpClient();
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(60);
client.BaseAddress = new Uri(Utils.baseUrl);
Dictionary<string, string> dataToSend = new Dictionary<string, string>();
dataToSend.Add("session", Utils.loginKey);
dataToSend.Add("eventId", idEvent+"");
dataToSend.Add("status", status.ToString());
string jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(dataToSend, new KeyValuePairConverter());
var contentVar = new StringContent(jsonData, Encoding.UTF8, "application/json");
try
{
HttpResponseMessage response = await client.PostAsync("/UpdateEvent", contentVar);
var result = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
string contents = await response.Content.ReadAsStringAsync();
var resultJson = JObject.Parse(result);
if ((int)resultJson["status"] == 0)
{
await base.DisplayAlert((string)resultJson["msg"], "", "OK");
return;
}
else if ((int)resultJson["status"] == 1)
{
//I'm currently trying to reload the whole view, before this was calling the method above.
await this.mainPage.Navigation.PushAsync(new NavigationPage(new MasterMenu.MainPage()));
await getRequests();
}
else
{
await base.DisplayAlert("Error procesando la solicitud, intente más tarde", "", "Ok");
return;
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Error update request: {0}", ex);
}
await Task.Delay(1);
}
If I leave it like that, UI will not update. Please help me, as I've been struggling with this issue for 2 days now. It happens exclusively on iOS.
The issue was fixed after updating iOS. The problem was caused because of a buggy iOS version that had problems indexing objects. After updating, everything ran as smoothly as usual. If anyone runs into this issue (exclusively on iOS), try updating both iOS and Xamarin Forms.

AcquireTokenByAuthorizationCodeAsync does not complete

Here is my code. It's been over a month I'm trying to add calendars in Outlook but nothing is working :( please help. The function AcquireTokenByAuthorizationCodeAsync never completes. And the token.Result is always null
string authority = ConfigurationManager.AppSettings["authority"];
string clientID = ConfigurationManager.AppSettings["clientID"];
Uri clientAppUri = new Uri(ConfigurationManager.AppSettings["clientAppUri"]);
string serverName = ConfigurationManager.AppSettings["serverName"];
var code = Request.Params["code"];
AuthenticationContext ac = new AuthenticationContext(authority, true);
ClientCredential clcred = new ClientCredential(clientid, secretkey);
//ac = ac.AcquireToken(serverName, clientID, clientAppUri, PromptBehavior.Auto);
//string to = ac.AcquireToken(serverName, clientID, clientAppUri, PromptBehavior.Auto).AccessToken;
var token = ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/");
string newtoken = token.Result.AccessToken;
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = new Uri("https://outlook.office365.com/" + "ews/exchange.asmx");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(token.Result.AccessToken);
exchangeService.FindFolders(WellKnownFolderName.Root, new FolderView(10));
Appointment app = new Appointment(exchangeService);
app.Subject = "";
app.Body = "";
app.Location = "";
app.Start = DateTime.Now;
app.End = DateTime.Now.AddDays(1);
app.Save(SendInvitationsMode.SendToAllAndSaveCopy);
I have experienced this issue when I call the AcquireTokenByAuthorizationCodeAsync using result instead of using await key words and all these code was in a asynchronously controller in the MVC.
If you are in the same scenario, you can fix this issue by two ways:
1.Always using the async, await like code below:
public async System.Threading.Tasks.Task<ActionResult> About()
{
...
var result =await ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/");
var accessToken = result.AccessToken;
...
}
2.Use the synchronization controller:
public void About()
{
...
var result =ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:2694/GetAuthCode/Index/"), clcred, resource: "https://graph.microsoft.com/").Result;
var accessToken = result.AccessToken;
...
}

Silverlight 3 File Dialog Box

Ok - I have a WCF Service which reads an excel file from a certain location and strips the data into an object. What I need is the ability to allow users of my program to Upload an excel sheet to the file location that my Service uses.
Alternitivley I could pass the Uploaded excel sheet to the service directly.
Can anyone help with this. My service code is:
public List<ImportFile> ImportExcelData(string FileName)
{
//string dataSource = Location + FileName;
string dataSource = Location;
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + dataSource.ToString() + ";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
con.Open();
var data = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
var sheetName = data.Rows[0]["TABLE_NAME"].ToString();
OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" + sheetName + "] WHERE Status = '4'", con);
OleDbDataAdapter oleda = new OleDbDataAdapter();
oleda.SelectCommand = cmd;
DataSet ds = new DataSet();
oleda.Fill(ds, "Employees");
DataTable dt = ds.Tables[0];
var _impFiles = new List<ImportFile>();
foreach (DataRow row in dt.Rows)
{
var _import = new ImportFile();
_import.PurchaseOrder = row[4].ToString();
try
{
var ord = row[8].ToString();
DateTime dati = Convert.ToDateTime(ord);
_import.ShipDate = dati;
}
catch (Exception)
{
_import.ShipDate = null;
}
ImportFile additionalData = new ImportFile();
additionalData = GetAdditionalData(_import.PurchaseOrder);
_import.NavOrderNo = additionalData.NavOrderNo;
_import.IsInstall = additionalData.IsInstall;
_import.SalesOrderId = additionalData.SalesOrderId;
_import.ActivityID = additionalData.ActivityID;
_import.Subject = additionalData.Subject ;
_import.IsMatched = (_import.ShipDate != null & _import.NavOrderNo != "" & _import.NavOrderNo != null & _import.ShipDate > DateTime.Parse("01/01/1999") ? true : false);
_import.UpdatedShipToField = false;
_import.UpdatedShipToFieldFailed = false;
_import.CreateNote = false;
_import.CreateNoteFailed = false;
_import.CompleteTask = false;
_import.CompleteTaskFailed = false;
_import.FullyCompleted = 0;
_import.NotCompleted = false;
_impFiles.Add(_import);
}
oleda.Dispose();
con.Close();
//File.Delete(dataSource);
return _impFiles;
}
You will want to modify your service to accept a Stream instead of a filename, then you can save if off to a file (or parse it directly from the Stream, although I don't know how to do that).
Then in your Silverlight app you could do something like this:
private void Button_Click(object sender, RoutedEventArgs ev)
{
var dialog = new OpenFileDialog();
dialog.Filter = "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm|All Files (*.*)|*.*";
if (dialog.ShowDialog() == true)
{
var fileStream = dialog.File.OpenRead();
var proxy = new WcfService();
proxy.ImportExcelDataCompleted += (s, e) =>
{
MessageBox.Show("Import Data is at e.Result");
// don't forget to close the stream
fileStream.Close();
};
proxy.ImportExcelDataAsync(fileStream);
}
}
You could also have your WCF service accept a byte[] and do something like this.
private void Button_Click(object sender, RoutedEventArgs ev)
{
var dialog = new OpenFileDialog();
dialog.Filter = "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm|All Files (*.*)|*.*";
if (dialog.ShowDialog() == true)
{
var length = dialog.File.Length;
var fileContents = new byte[length];
using (var fileStream = dialog.File.OpenRead())
{
if (length > Int32.MaxValue)
{
throw new Exception("Are you sure you want to load > 2GB into memory. There may be better options");
}
fileStream.Read(fileContents, 0, (int)length);
}
var proxy = new WcfService();
proxy.ImportExcelDataCompleted += (s, e) =>
{
MessageBox.Show("Import Data is at e.Result");
// no need to close any streams this way
};
proxy.ImportExcelDataAsync(fileContents);
}
}
Update
Your service could look like this:
public List<ImportFile> ImportExcelData(Stream uploadedFile)
{
var tempFile = HttpContext.Current.Server.MapPath("~/uploadedFiles/" + Path.GetRandomFileName());
try
{
using (var tempStream = File.OpenWrite(tempFile))
{
uploadedFile.CopyTo(tempStream);
}
//string dataSource = Location + FileName;
string dataSource = tempFile;
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + dataSource.ToString() +
";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
con.Open();
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}
Thanks Bendewey that was great. Had to amend it slightly -
My Service:
var tempFile = #"c:\temp\" + Path.GetRandomFileName();
try
{
int length = 256;
int bytesRead = 0;
Byte[] buffer = new Byte[length];
// write the required bytes
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
do
{
bytesRead = uploadedFile.Read(buffer, 0, length);
fs.Write(buffer, 0, bytesRead);
}
while (bytesRead == length);
}
uploadedFile.Dispose();
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + dataSource.ToString() + ";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
Thanks Again for your help

Resources