I am using .NET Framework 4.6.1.
I have a controller in my web api, where I have static HttpClient to handle all the http requests. After I hosted my app on IIS, approximately once in a month, I get the following exception for all the incoming request to my app:
System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.Enter(Object obj)
at System.Net.Http.Headers.HttpHeaders.ParseRawHeaderValues(String name, HeaderStoreItemInfo info, Boolean removeEmptyHeader)
at System.Net.Http.Headers.HttpHeaders.AddHeaders(HttpHeaders sourceHeaders)
at System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders)
at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
at Attributes.Controllers.AttributesBaseController.<UpdateAttributes>d__6.MoveNext() in D:\Git\PortalSystem\Attributes\Controllers\AttributesBaseController.cs:line 42
If I restart the app pool on IIS, everything starts to work fine again. Here is the code that I have:
public class AttributesBaseController : ApiController
{
[Inject]
public IPortalsRepository PortalsRepository { get; set; }
private static HttpClient Client = new HttpClient(new HttpClientHandler { Proxy = null, UseProxy = false })
{ Timeout = TimeSpan.FromSeconds(double.Parse(WebConfigurationManager.AppSettings["httpTimeout"])) };
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
protected async Task UpdateAttributes(int clientId, int? updateAttrId = null)
{
try
{
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
#region Update Client Dossier !!! BELOW IS LINE 42 !!!!
using (var response = await Client.PutAsync(new Uri(WebConfigurationManager.AppSettings["dossier"] + "api/dossier?clientId=" + clientId), null))
{
if (!response.IsSuccessStatusCode)
{
logger.Error($"Dossier update failed");
}
}
#endregion
#region Gather Initial Info
var checkSystems = PortalsRepository.GetCheckSystems(clientId);
var currentAttributes = PortalsRepository.GetCurrentAttributes(clientId, checkSystems);
#endregion
List<Task> tasks = new List<Task>();
#region Initialize Tasks
foreach (var cs in checkSystems)
{
if (!string.IsNullOrEmpty(cs.KeyValue))
{
tasks.Add(Task.Run(async () =>
{
var passedAttributes = currentAttributes.Where(ca => ca.SystemId == cs.SystemId && ca.AttributeId == cs.AttributeId &&
(ca.SysClientId == cs.KeyValue || ca.OwnerSysClientId == cs.KeyValue)).ToList();
if (cs.AttributeId == 2 && (updateAttrId == null || updateAttrId == 2))
{
await UpdateOpenWayIndividualCardsInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 3 && (updateAttrId == null || updateAttrId == 3))
{
await UpdateEquationAccountsInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 8 && (updateAttrId == null || updateAttrId == 8))
{
await UpdateOpenWayCorporateInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 9 && (updateAttrId == null || updateAttrId == 9))
{
await UpdateEquationDealsInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 10 && (updateAttrId == null || updateAttrId == 10))
{
await UpdateOpenWayIndividualCardDepositsInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 16 && (updateAttrId == null || updateAttrId == 16))
{
await UpdateOpenWayBonusInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 17 && (/*updateAttrId == null ||*/ updateAttrId == 17))
{
await UpdateExternalCardsInfo(passedAttributes, cs, clientId);
}
if (cs.AttributeId == 18 && (updateAttrId == null || updateAttrId == 18))
{
await UpdateCRSInfo(passedAttributes, cs, clientId);
}
else if (cs.AttributeId == 22 && (updateAttrId == null || updateAttrId == 22))
{
await UpdateCardInsuranceInfo(passedAttributes, cs, clientId);
}
}));
}
}
#endregion
// Run all tasks
await Task.WhenAny(Task.WhenAll(tasks.ToArray()), Task.Delay(TimeSpan.FromSeconds(double.Parse(WebConfigurationManager.AppSettings["taskWaitTime"]))));
}
catch (Exception ex)
{
logger.Error(ex);
}
}
}
Can anyone give me advice/help to figure out the problem? I just don't know whether the problem is in the way I am using HttpClient with tasks or something bad happens on IIS.
Looking at the implementation of DefaultRequestHeaders, we can see that it uses a simple Dictionary to store the headers:
private Dictionary<string, HttpHeaders.HeaderStoreItemInfo> headerStore;
DefaultRequestHeaders.Accept.Clear just removes the key from the dictionary, without any kind of synchronization:
public bool Remove(string name)
{
this.CheckHeaderName(name);
if (this.headerStore == null)
return false;
return this.headerStore.Remove(name);
}
Dictionary.Remove isn't thread-safe, unpredictable behavior can happen if you access the dictionary during this operation.
Now if we look at the ParseRawHeaderValues method in your stacktrace:
private bool ParseRawHeaderValues(string name, HttpHeaders.HeaderStoreItemInfo info, bool removeEmptyHeader)
{
lock (info)
{
// stuff
}
return true;
}
We can see that the error would be cause by info to be null. Now looking at the caller:
internal virtual void AddHeaders(HttpHeaders sourceHeaders)
{
if (sourceHeaders.headerStore == null)
return;
List<string> stringList = (List<string>) null;
foreach (KeyValuePair<string, HttpHeaders.HeaderStoreItemInfo> keyValuePair in sourceHeaders.headerStore)
{
if (this.headerStore == null || !this.headerStore.ContainsKey(keyValuePair.Key))
{
HttpHeaders.HeaderStoreItemInfo headerStoreItemInfo = keyValuePair.Value;
if (!sourceHeaders.ParseRawHeaderValues(keyValuePair.Key, headerStoreItemInfo, false))
{
if (stringList == null)
stringList = new List<string>();
stringList.Add(keyValuePair.Key);
}
else
this.AddHeaderInfo(keyValuePair.Key, headerStoreItemInfo);
}
}
if (stringList == null)
return;
foreach (string key in stringList)
sourceHeaders.headerStore.Remove(key);
}
Long story short, we iterate the dictionary in DefaultRequestHeaders (that's sourceHeaders.headerStore) and copy the headers into the request.
Summing it up, at the same time we have a thread iterating the contents of the dictionary, and another adding/removing elements. This can lead to the behavior you're seeing.
To fix this, you have two solutions:
Initialize DefaultRequestHeaders in a static constructor, then never change it:
static AttributesBaseController
{
Client = new HttpClient(new HttpClientHandler { Proxy = null, UseProxy = false })
{
Timeout = TimeSpan.FromSeconds(double.Parse(WebConfigurationManager.AppSettings["httpTimeout"]))
};
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
Use SendAsync with your custom headers instead of PutAsync:
var message = new HttpRequestMessage(HttpMethod.Put, new Uri(WebConfigurationManager.AppSettings["dossier"] + "api/dossier?clientId=" + clientId));
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var response = await Client.SendAsync(message))
{
// ...
}
Just for fun, a small repro:
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var storeField = typeof(HttpHeaders).GetField("headerStore", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo valueField = null;
var store = (IEnumerable)storeField.GetValue(client.DefaultRequestHeaders);
foreach (var item in store)
{
valueField = item.GetType().GetField("value", BindingFlags.Instance | BindingFlags.NonPublic);
Console.WriteLine(valueField.GetValue(item));
}
for (int i = 0; i < 8; i++)
{
Task.Run(() =>
{
int iteration = 0;
while (true)
{
iteration++;
try
{
foreach (var item in store)
{
var value = valueField.GetValue(item);
if (value == null)
{
Console.WriteLine("Iteration {0}, value is null", iteration);
}
break;
}
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
catch (Exception) { }
}
});
}
Console.ReadLine();
Output:
System.Net.Http.Headers.HttpHeaders+HeaderStoreItemInfo
Iteration 137, value is null
Reproducing the issue may take a few tries because threads tend to get stuck in an infinite loop when accessing the dictionary concurrently (if it happens on your webserver, ASP.NET will abort the thread after the timeout elapses).
Related
Hi I have Controller method as below
[HttpPost]
public JsonResult Post(string vehiclesString, string Entity, int EntityId, ApplicationUser CurrentUser)
{
//https://stackify.com/understanding-asp-net-performance-for-reading-incoming-data/
List<Vehicle> vehicles = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Vehicle>>(vehiclesString);
InputFieldController c = new InputFieldController();
var errors = new List<string>();
try
{
//let's get model for each of the input field
InputFieldController icController = new InputFieldController();
List<InputField> fields = icController.GetNamesValues("VehicleField", -1, "Vehicle", 0);
foreach (Vehicle vehicle in vehicles)
{
//convert array of strings into array of input fields
if (fields.Count != vehicle.ValueStrings.Count)
{
throw new Exception("Vehicle columns mismatch. Expected "
+ fields.Count + " fields, but received " + vehicle.ValueStrings.Count);
}
for (int i = 0; i < fields.Count; i++)
{
InputField field = fields[i];
string cell = vehicle.ValueStrings[i];
if ((cell != null || cell != String.Empty) && (field.Type == "radio" || field.Type == "dropdown"))
{
var f = field.InputDropdowns.Where(x => x.Name == cell).FirstOrDefault();
if (f != null)
{
field.InputValue.InputDropdownId = f.InputDropdownId;
}
else
field.InputValue.InputDropdownId = null;
}
else
{
field.InputValue.Value = cell;
}
vehicle.Values.Add(field);
}
vehicle.Blob = Newtonsoft.Json.JsonConvert.SerializeObject(vehicle.Values);
Vehicle v = new Vehicle();
if (vehicle.VehicleId == 0)
{
v = this.DomainLogicUnitOfWork.VehicleManager.Create(vehicle, Entity, EntityId);
}
}
JsonResult data = Json(new
{
success = true,
});
List<Vehicle> vehiclesList = this.DomainLogicUnitOfWork.VehicleManager.List(Entity, EntityId);
if (vehiclesList != null)
foreach (Vehicle v in vehiclesList)
{
if ((v != null) && (v.Blob != null))
v.Values = Newtonsoft.Json.JsonConvert.DeserializeObject<List<InputField>>(v.Blob);
}
//Task task = Task.Run(async () => await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId));
/*
* Here I have to call the this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(string Entity, int EntityId) asynchronously
* but return the data without waiting for the CreateOrUpdate to complete
*/
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(vehiclesList, Entity, EntityId);
});
return data;
}
catch (Exception ex)
{
LogHandler.LogError(9000, "Error updating input fields", ex);
errors.Add("Error 9000:" + ex.Message);
return Json(new
{
error = ex.Message
});
}
}
And I have CreateOrUpdate method defined as below in VehicleInfoManager class
public async Task CreateOrUpdate(string Entity, int EntityId)
{
//do some stuff
var task = Task.Run(() => Test(Entity, EntityId));
//do other stuff
await task;
//some more stuff
}
And Test method is as follows
private void Test(string Entity, int EntityId)
{
List<VehicleInfo> addList; List<VehicleInfo> updateList;
try
{
this.GetAddAndUpdateList(Entity, EntityId, out addList, out updateList);
if ((addList != null) && (addList.Count > 0))
using (var cont = this.UnitOfWork.Context)
{
foreach (var a in addList)
{
cont.VehicleInfos.Add(a);
}
cont.SaveChanges();
}
if ((updateList != null) && (updateList.Count > 0))
using (var cont = this.UnitOfWork.Context)
{
foreach (var a in updateList)
{
var aa = cont.VehicleInfos?.Where(x => x.VehicleInfoId == a.VehicleInfoId)?.FirstOrDefault();
aa.Address_City = a.Address_City;
aa.Address_Country = a.Address_Country;
aa.Address_StateCode = a.Address_StateCode;
aa.Address_Street1 = a.Address_Street1;
aa.Address_Street2 = a.Address_Street2;
aa.Address_Zip = a.Address_Zip;
aa.ChassisYear = a.ChassisYear;
aa.EngineFamilyName = a.EngineFamilyName;
aa.Entity = a.Entity;
aa.EntityId = a.EntityId;
aa.InputFieldEntity = a.InputFieldEntity;
aa.InputFieldEntityId = a.InputFieldEntityId;
aa.InputFieldGroup = a.InputFieldGroup;
aa.LicensePlate = a.LicensePlate;
aa.Manufacturer = a.Manufacturer;
aa.ModelYear = a.ModelYear;
aa.PurchasedDate = a.PurchasedDate;
aa.RegHoldClearBy = a.RegHoldClearBy;
aa.RegHoldClearDate = a.RegHoldClearDate;
aa.RegHoldComment = a.RegHoldComment;
aa.RegHoldSet = a.RegHoldSet;
aa.RegHoldSetBy = a.RegHoldSetBy;
aa.RegHoldSetDate = a.RegHoldSetDate;
aa.TrailerPlate = a.TrailerPlate;
aa.UpdatedBy = a.UpdatedBy;
aa.UpdatedDate = a.UpdatedDate;
aa.VehicleId = a.VehicleId;
aa.VehicleOperator = a.VehicleOperator;
aa.VehicleOwner = a.VehicleOwner;
aa.VIN = a.VIN;
}
cont.SaveChanges();
}
}
catch (Exception ex)
{
ARB.Logging.LogHandler.LogError(9001, "CreateOrUpdate(string Entity, int EntityId) in class VehicleInfoManager", ex);
throw ex;
}
}
What I want is, I want two things here
the Post method to call or start the CreateOrUpdate method as background call but instead of waiting until the CreateOrUpdate method finishes, it should return the result data to UI and continue the big task CreateOrUpdate in the background.
Is there anyway to start the background method CreateOrUpdate after sometime like (10 mins etc) post method returns to UI, if it can't be done, its OK we don't have to worry but just asking if there is anyway to trigger this from within the same application
When I implemented it in the above way, even after using System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem I am still getting the null http context at the following location
user = System.Web.HttpContext.Current.User.Identity.Name; url = System.Web.HttpContext.Current.Request.Url.ToString();
System.Web.HttpContext.Current is coming out as null.
and the application is breaking,
I chaned my async call to the following to use HostingEnvironment.QueueBackgroundWorkItem, still the same htt current context is coming out as null, any help please
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId);
});
Thank you
System.Web.HttpContext.Current is maintained by the ASP.NET's SynchronizationContext.
When you start a new Task, the code will be executing on another thread pool thread without a SynchronizationContext and the value of System.Web.HttpContext.Current is not safe to use, whatever its value.
When the execution of the action method (Post) ends, the request will end and the HttpContext instance will be invalid, even if you mange to get a reference to it.
Also, there is no guarantee that that code you posted to the thread pool will run to complete, since it's out of ASP.NET control and ASP.NET won't be aware of it if IIS decides, for some reason, to recycle the application pool or the web application.
If you to post background work, use HostingEnvironment.QueueBackgroundWorkItem. Beware if its constraints.
Okay so I'm still quite new to coding and mostly only knows the basics. I have never worked with API. I'm trying to make a program that gets the number of subs from PewDiePie and Cocomelon and compare them.
namespace PewdiepieVsCoco
{
class Program
{
static void Main(string[] args)
{
try
{
var len = args?.Length;
if (len == null || len.Value == 0)
{
PrintStart();
return;
}
var pdpSubCount = args[0];
var pdpSub = GetPDPSubcount(pdpSubCount).Result;
PrintPDPResult(pdpSub);
}
catch (AggregateException agg)
{
foreach (var e in agg.Flatten().InnerExceptions)
Console.WriteLine(e.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
private static async Task<dynamic> GetPDPSubcount(string pdpSubCount)
{
var parameters = new Dictionary<String, String>
{
["key"] = ConfigurationManager.AppSettings["APIKey"],
["channelsId"] = "UC-lHJZR3Gqxm24_Vd_AJ5Yw",
["part"] = "statistics",
["forUsername"] = "PewDiePie",
["fields"] = "items/statistics(subscriberCount)"
};
var baseUrl = "https://www.googleapis.com/youtube/v3/channels";
var fullUrl = MakeUrlWithQuery(baseUrl, parameters);
var pdpSub = await new HttpClient().GetStringAsync(fullUrl);
if (pdpSub != null)
{
//Does the thing
return JsonConvert.DeserializeObject(pdpSubCount);
}
return default(dynamic);
}
private static string MakeUrlWithQuery(string baseUrl,
IEnumerable<KeyValuePair<string, string>> parameters)
{
if (string.IsNullOrEmpty(baseUrl))
throw new ArgumentException(nameof(baseUrl));
if (parameters == null || parameters.Count() == 0)
return baseUrl;
return parameters.Aggregate(baseUrl,
(accumulated, kvp) => string.Format($"{accumulated}{kvp.Value}&"));
}
private static void PrintPDPResult(dynamic pdpSub)
{
Console.WriteLine($"PewDiePie currently have: {pdpSub} subscribers");//insert subs
}
private static void PrintStart()
{
Console.WriteLine("The war is on!");
Thread.Sleep(500);
Console.WriteLine("PewDiePie Vs Cocomelon – Nursery Rhymes");
Thread.Sleep(500);
}
}
}
Here is the code, I have followed what an Indian dude did in a YT video so some things I don't know what do but I have an idea on what's going on.
Your program is likely returning before making any API calls because you aren't executing it with any command line arguments. Remove this block of code, so it continues without returning early.
var len = args?.Length;
if (len == null || len.Value == 0)
{
PrintStart();
return;
}
Also, pdpSubCount should not be a function argument, and should not be passed into DeserializeObject. You should be deserializing the response from the api call
return JsonConvert.DeserializeObject(pdpSub)
Just trying to do normal query on ef but getting below error message
"A second operation started on this context before a previous asynchronous operation completed"
I am using await for all aync call but not sure what could be causing this issue.
here' code
var sids = await context.Sites.Where(s => slist.Contains(s.JVSiteID)).ToListAsync();
var header = await context.GenericCsvHeaders.FirstOrDefaultAsync(x => x.Header == csvh) ?? new GenericCsvHeader { Header = csvh };
context.Entry<GenericCsvHeader>(header).State = (header.GenericCsvHeaderID == 0) ? EntityState.Added : EntityState.Unchanged;
var sa = await context.SAs.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.SAName)).Select(x => new { x.SAName, x.SACode, x.SAID }).ToListAsync();
if (sa.Count > 0)
sasitelist = await context.Site_SA.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var az = await context.Azimuths.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.AzimuthName)).Select(x => new { x.AzimuthName, x.AzimuthID }).ToListAsync();
if (az.Count > 0)
azsitelist = await context.Site_Azimuth.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var rows = new List<dynamic>(); //getting this list from csv file via csvHelper
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
UpdateSite(s, r, map);
}
private async void UpdateSite(Site site, dynamic csvSite, IDictionary<string, string> map)
{
context.Entry(site).State = (site.StateID == 0) ? EntityState.Added : EntityState.Modified;
site.SiteForAuditTrail = site;
if (map.ContainsKey("SiteName") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite,"SiteName")))
site.SiteName = GetStringOfDynamic(map,csvSite, "SiteName");
if (map.ContainsKey("State") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite, "State")))
{
//getting exception at below line
var state = (await GetRefTypeList<State>(x => x.StateCode == GetStringOfDynamic(map,csvSite, "State"))) ?? new State { StateCode = GetStringOfDynamic(map,csvSite, "State") };
context.Entry(state).State = (state.StateID == 0) ? EntityState.Added : EntityState.Unchanged;
site.State = state;
state.SiteForAuditTrail = site;
}
}
private async Task<T> GetRefTypeList<T>(Func<T, bool> expression) where T : EntityBase
{
if (!refTypes.ContainsKey(typeof(T).Name))
refTypes[typeof(T).Name] = (await context.Set<T>().Where(x => x.StatusID == Data.Enum.Status.Active).ToListAsync());
return (refTypes[typeof(T).Name] as List<T>)?.FirstOrDefault(expression);
}
private string GetStringOfDynamic(IDictionary<string, string> map,dynamic obj, string propertyName)
{
if (((IDictionary<String, Object>)obj).ContainsKey(map[propertyName]))
return (((IDictionary<String, Object>)obj)[map[propertyName]]??"").ToString();
return "";
}
found problem, was missing await when calling UpdateSite
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
**await** UpdateSite(s, r, map);
}
You are having a race condition here. While you are using await you have other code executing potentially before the variable sids, header, sa and az, have been returned.
You should refactor your code so that the remaining code is not executed until these variables are returned. You can do this by using a task and then using the extension WhenAll which will ensure each await has been completed before proceeding. Here is a link to MS docs on Task and WhenAll implementation
I have two dialogs called from root dialog based on the prompt response.
Both dialogs internally prepare request and call a service class. Weird thing is one dialog resumes with response from service but the other though receives response from service is not resuming.
Below method works fine and resumes the call at if (searchResult != null && searchResult.Item1 != null)
public virtual async Task MessageRecievedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Text == "quit")
{
context.Done<object>(null);
}
else
{
try
{
var srm = new CoveoRestSearchService.CoveoSearchRequestModel
{
Query = message.Text,
EnableDidYouMean = true,
QuerySource = CoveoRestSearchService.Constants.SWEATERSSOURCE
};
var searchResult = await csp.PerformSearch(srm);
if (searchResult != null && searchResult.Item1 != null)
{
await CardUtil.showHeroCard(message, searchResult.Item1);
}
else
{
await context.PostAsync($"No search results for {message.Text} found");
}
await PostSearchUsageAnalyticsToCoveo(context, srm, searchResult.Item2);
}
catch (Exception e)
{
Debug.WriteLine($"Error when searching : {e.Message}");
}
finally {
context.Wait(MessageRecievedAsync);
}
}
}
This below one though looks identical is not resuming the if (response != null)
public virtual async Task MessageRecievedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Text == "quit")
{
context.Done<object>(null);
}
else
{
try
{
var currentDate = DateTime.UtcNow;
string toDate = currentDate.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string fromDate = currentDate.AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
MetricsQuery = MetricsQuery.Replace("{fromISODate}", fromDate);
MetricsQuery = MetricsQuery.Replace("{toISODate}", toDate);
MetricsQuery = MetricsQuery.Replace("{term}", message.Text);
var response = await cuawc.MetricSearchUsageToCoveoAsync(MetricsQuery
.Replace(" ", "%20")
.Replace("!=", "!%3D")
.Replace("==", "%3D%3D")
.Replace(":", "%3A"));
if (response != null)
{
var message_from_bot = context.MakeMessage();
}
//message_from_bot.Attachments = new List<Attachments>();
await context.PostAsync("Please enter the search term for metrics");
}
catch (Exception e)
{
Debug.WriteLine($"Error when pulling metrics : {e.Message}");
}
finally
{
context.Wait(MessageRecievedAsync);
}
}
}
Struggling from past 2 days to figure what is wrong!!
I'm trying to return an appropriate error message when a user tries to call the service when their authorization token has expired or if they have an invalid token.
The problem I'm having is the first time it is called, the message is sent properly, but after the first time the SendAsync method is called 4 times and the message data returns null.
I'm confused as to why it loops 4 times, and I tried stepping through it, but I can't get any further in the code.
Here is the code:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Headers != null)
{
// ....
if (request.Headers.GetValues(CustomTokenHeader).FirstOrDefault() == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
var authHeader = request.Headers.GetValues(CustomTokenHeader).FirstOrDefault();
if (String.IsNullOrWhiteSpace(authHeader))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//authenticate token
return HandleTokenAuthentication(request, cancellationToken, authHeader);
}
}
static Task<T> FromResult<T>(T t)
{
var tcs = new TaskCompletionSource<T>();
tcs.SetResult(t);
return tcs.Task;
}
private Task<HttpResponseMessage> HandleTokenAuthentication(HttpRequestMessage request, CancellationToken cancellationToken, string authHeader)
{
//parse token
var token = ParseToken(authHeader);
if (String.IsNullOrWhiteSpace(token))
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//decrypt token
var tokenInfo = DecryptToken(token);
if (tokenInfo == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
//validate token
var claims = ValidateToken(tokenInfo, token);
if (claims == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedTokenExpired);
}
var principal = CheckCustomAuthorization(claims);
if (principal == null)
{
//unauthorized response(401)
return FromResult(_unauthorizedResponse);
}
if (!principal.Identity.IsAuthenticated)
{
var loginFailureMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent(((AgencyClaims)principal.Identity).LoginFailureReason)
};
return FromResult(loginFailureMessage);
}
//assign principal
Thread.CurrentPrincipal = principal;
return base.SendAsync(request, cancellationToken)
.ContinueWith(task => AuthorizedResponse(request, task.Result));
}
static HttpResponseMessage AuthorizedResponse(HttpRequestMessage request, HttpResponseMessage response)
{
if ((request.Method == HttpMethod.Get && response.StatusCode == HttpStatusCode.OK
&& !response.Headers.Contains(CustomTokenHeader))
|| (request.Method == HttpMethod.Post && response.StatusCode == HttpStatusCode.Created
&& !response.Headers.Contains(CustomTokenHeader)))
{
var token = ((AgencyClaims) Thread.CurrentPrincipal.Identity).Token;
response.Headers.Add(CustomTokenHeader, Convert.ToBase64String(Encoding.ASCII.GetBytes(token)));
}
return response;
}
readonly HttpResponseMessage _unauthorizedResponse =
new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("PROPER ERROR MESSAGE")};
Successful response:
<data contentType="text/plain; charset=utf-8" contentLength="21"><![CDATA[Authentication failed]]></data>
Here is the response after the first successful response:
<data contentType="null" contentLength="0"><![CDATA[]]></data>
Here is part of the request:
GET http://localhost:20559/api/Service?Name=Jack HTTP/1.1
Ok, I was able to resolve the issue. The _unauthorizedResponse class variable is somehow allowing the code to run once through successfully, but not a second time. This issue is not related to the readonly modifier, since it still doesn't work without it. I'm not sure how this works (and maybe someone on here can explain), but by moving them into the local scope within the method it is able to run correctly each time.