Related
I have a valid consumer key and I'm unable to get Interactive Brokers Web API OAuth to work.
{"error":"id: 3931, error: invalid consumer","statusCode":401}
The endpoint I'm trying to get to work: https://www.interactivebrokers.com/webtradingapi/doc.html#tag/OAuth/paths/~1oauth~1request_token/post
OAuth v1.0a specification https://oauth.net/core/1.0a/#auth_header_authorization
Am I missing something in my implementation?
It's also on GitHub.
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://www.interactivebrokers.com/tradingapi/v1/")
};
var restClient = new IBRestClient(httpClient);
var response = await restClient.RequestTokenAsync("xxxxx");
Console.WriteLine($"Response: {response}");
Console.ReadLine();
public sealed class IBRestClient
{
private readonly HttpClient _httpClient;
public IBRestClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async ValueTask<string> RequestTokenAsync(string consumerKey)
{
const string requestUri = "oauth/request_token";
var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
var baseUrl = _httpClient.BaseAddress!.AbsoluteUri;
var authorizationHeader = OAuthHelper.GetAuthorizationHeader($"{baseUrl}{requestUri}", "POST", consumerKey);
var authSplit = authorizationHeader.Split(' ');
request.Headers.Authorization = new AuthenticationHeaderValue(authSplit[0], authSplit[1]);
var response = await _httpClient.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
public static class OAuthHelper
{
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
public static string GetAuthorizationHeader(string uri, string method, string consumerKey)
{
var oauthParameters = new Dictionary<string, string>
{
{ "oauth_consumer_key", consumerKey },
{ "oauth_signature_method", "RSA-SHA256" },
{ "oauth_timestamp", GetTimestamp() },
{ "oauth_nonce", GetNonce() },
{ "oauth_callback", "oob" }
};
// The request parameters are collected, sorted and concatenated into a normalized string
var queryParameters = ExtractQueryParams(uri);
var oauthParamString = GetOAuthParamString(queryParameters, oauthParameters);
var baseUri = GetBaseUriString(uri);
// Signature Base String
var signatureBaseString = GetSignatureBaseString(baseUri, method, oauthParamString);
var pem = File.ReadAllText("private_encryption.pem");
var signingKey = RSA.Create();
signingKey.ImportFromPem(pem);
var signature = SignSignatureBaseString(signatureBaseString, Encoding.UTF8, signingKey);
oauthParameters.Add("oauth_signature", signature);
// Constructs and returns the Authorization header
var sb = new StringBuilder();
foreach (var param in oauthParameters)
{
sb
.Append(sb.Length == 0 ? "OAuth " : ",")
.Append(param.Key)
.Append("=\"")
.Append(ToUriRfc3986(param.Value))
.Append('"');
}
return sb.ToString();
}
/// <summary>
/// Parse query parameters out of the URL.
/// </summary>
private static Dictionary<string, List<string>> ExtractQueryParams(string uri)
{
var queryParamCollection = new Dictionary<string, List<string>>();
var beginIndex = uri.IndexOf('?');
if (beginIndex <= 0)
{
return queryParamCollection;
}
var rawQueryString = uri[beginIndex..];
var decodedQueryString = Uri.UnescapeDataString(rawQueryString);
var mustEncode = !decodedQueryString.Equals(rawQueryString);
var queryParams = rawQueryString.Split('&', '?');
foreach (var queryParam in queryParams)
{
if (string.IsNullOrEmpty(queryParam))
{
continue;
}
var separatorIndex = queryParam.IndexOf('=');
var key = separatorIndex < 0 ? queryParam : Uri.UnescapeDataString(queryParam[..separatorIndex]);
var value = separatorIndex < 0
? string.Empty
: Uri.UnescapeDataString(queryParam[(separatorIndex + 1)..]);
var encodedKey = mustEncode ? ToUriRfc3986(key) : key;
var encodedValue = mustEncode ? ToUriRfc3986(value) : value;
if (!queryParamCollection.ContainsKey(encodedKey))
{
queryParamCollection[encodedKey] = new List<string>();
}
queryParamCollection[encodedKey].Add(encodedValue);
}
return queryParamCollection;
}
/// <summary>
/// Lexicographically sorts all parameters and concatenates them into a string.
/// </summary>
private static string GetOAuthParamString(IDictionary<string, List<string>> queryParameters,
IDictionary<string, string> oauthParameters)
{
var sortedParameters = new SortedDictionary<string, List<string>>(queryParameters, StringComparer.Ordinal);
foreach (var oauthParameter in oauthParameters)
{
sortedParameters[oauthParameter.Key] = new List<string> { oauthParameter.Value };
}
// Build the OAuth parameter string
var parameterString = new StringBuilder();
foreach (var parameter in sortedParameters)
{
var values = parameter.Value;
values.Sort(StringComparer.Ordinal); // Keys with same name are sorted by their values
foreach (var value in values)
{
parameterString
.Append(parameterString.Length > 0 ? "&" : string.Empty)
.Append(parameter.Key)
.Append('=')
.Append(value);
}
}
return parameterString.ToString();
}
/// <summary>
/// Normalizes the URL.
/// </summary>
private static string GetBaseUriString(string uriString)
{
var uri = new Uri(uriString);
var lowerCaseScheme = uri.Scheme.ToLower();
var lowerCaseAuthority = uri.Authority.ToLower();
var path = uri.AbsolutePath;
if (("http".Equals(lowerCaseScheme) && uri.Port == 80) || ("https".Equals(lowerCaseScheme) && uri.Port == 443))
{
// Remove port if it matches the default for scheme
var index = lowerCaseAuthority.LastIndexOf(':');
if (index >= 0)
{
lowerCaseAuthority = lowerCaseAuthority[..index];
}
}
if (string.IsNullOrEmpty(path))
{
path = "/";
}
return $"{lowerCaseScheme}://{lowerCaseAuthority}{path}"; // Remove query and fragment
}
/// <summary>
/// The Signature Base String is a consistent reproducible concatenation of the request elements into a single string.
/// </summary>
private static string GetSignatureBaseString(string baseUri, string httpMethod, string oauthParamString)
{
return httpMethod.ToUpper() // Uppercase HTTP method
+ "&" + ToUriRfc3986(baseUri) // Base URI
+ "&" + ToUriRfc3986(oauthParamString); // OAuth parameter string
}
/// <summary>
/// Signs the signature base string using an RSA private key.
/// </summary>
private static string SignSignatureBaseString(string baseString, Encoding encoding, RSA privateKey)
{
var hash = Sha256Digest(baseString, encoding);
var signedHashValue = privateKey.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signedHashValue);
}
/// <summary>
/// Percent encodes entities.
/// </summary>
private static string ToUriRfc3986(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}
var escaped = new StringBuilder(Uri.EscapeDataString(input));
string[] uriRfc3986EscapedChars = { "!", "*", "'", "(", ")" };
foreach (var escapedChar in uriRfc3986EscapedChars)
{
escaped.Replace(escapedChar, UriHelper.HexEscape(escapedChar[0]));
}
return escaped.ToString();
}
/// <summary>
/// Returns a cryptographic hash of the given input.
/// </summary>
private static byte[] Sha256Digest(string input, Encoding encoding)
{
var inputBytes = encoding.GetBytes(input);
return SHA256.HashData(inputBytes);
}
/// <summary>
/// Generates a 16 char random string for replay protection.
/// </summary>
private static string GetNonce()
{
var data = new byte[8];
Random.GetBytes(data);
return BitConverter.ToString(data).Replace("-", string.Empty).ToLower();
}
/// <summary>
/// Returns UNIX Timestamp.
/// </summary>
private static string GetTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
}
}
internal static class UriHelper
{
private static readonly char[] HexUpperChars =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
internal static string HexEscape(char character)
{
if (character > '\xff')
{
throw new ArgumentOutOfRangeException(nameof(character));
}
var chars = new char[3];
var pos = 0;
EscapeAsciiChar(character, chars, ref pos);
return new string(chars);
}
private static void EscapeAsciiChar(char ch, char[] to, ref int pos)
{
to[pos++] = '%';
to[pos++] = HexUpperChars[(ch & 0xf0) >> 4];
to[pos++] = HexUpperChars[ch & 0xf];
}
}
I have Pengwin (Linux distro optimalized for working with WSL - surely everybody know) with installed github-linguist, set up by tutorial in https://github.com/github/linguist/blob/master/README.md . Even I have use my Insider Windows instance, change version WSL for upcoming 2, but everything keep like before.
In Pengwin and Powershell cmd is everything OK:
wsl bash -c "github-linguist '/mnt/d/Documents/Visual Studio 2017/Projects/LearnCss/LearnCss/Program.cs'"
But by calling in C# is output empty (any other commmand like dir works):
static PowershellBuilder builder = new PowershellBuilder();
/// <summary>
/// Tested, working
/// For every command return at least one entry in result
/// </summary>
/// <param name="commands"></param>
/// <returns></returns>
public async static Task<List< List<string>>> InvokeAsync(IEnumerable<string> commands)
{
List<List<string>> returnList = new List<List<string>>();
PowerShell ps = null;
// After leaving using is closed pipeline, must watch for complete or
using (ps = PowerShell.Create())
{
foreach (var item in commands)
{
ps.AddScript(item);
var async = ps.BeginInvoke();
// Return for SleepWithRandomOutputConsole zero outputs
var psObjects = ps.EndInvoke(async);
returnList.Add(ProcessPSObjects(psObjects));
}
}
return returnList;
}
/// <summary>
/// Tested, working
/// For every command return at least one entry in result
/// </summary>
/// <param name="commands"></param>
/// <returns></returns>
public static List<List<string>> Invoke(IEnumerable<string> commands)
{
var result = InvokeAsync(commands);
result.Wait();
return result.Result;
}
private static List<string> ProcessPSObjects(ICollection<PSObject> pso)
{
List<string> output = new List<string>();
foreach (var item in pso)
{
if (item != null)
{
output.Add(item.ToString());
}
}
return output;
}
public static List<string> InvokeSingle(string command)
{
return Invoke(CA.ToListString(command))[0];
}
But with calling with Process.Start all works like a charm:
/// <summary>
/// If in A1 will be full path specified = 'The system cannot find the file specified'
/// A1 if dont contains extension, append exe
/// </summary>
/// <param name="exeFileNameWithoutPath"></param>
/// <param name="arguments"></param>
/// <returns></returns>
public static List<string> InvokeProcess(string exeFileNameWithoutPath, string arguments)
{
FS.AddExtensionIfDontHave(exeFileNameWithoutPath, AllExtensions.exe);
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
// Must contains only filename, not full path
pProcess.StartInfo.FileName = exeFileNameWithoutPath;
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = arguments;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Optional, recommended do not enter, then old value is not deleted and both paths is combined
//pProcess.StartInfo.WorkingDirectory = ;
//Start the process
pProcess.Start();
W32.EnableWow64FSRedirection(true);
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
var result = SH.GetLines(strOutput);
return result;
}
Calling code:
const string lang = "language:";
public static string DetectLanguageForFileGithubLinguist(string windowsPath)
{
string command = null;
string arguments = null;
// With WSL or WSL 2 not working. In both cases Powershell returns right values but in c# everything empty. Asked on StackOverflow
StringBuilder linuxPath = new StringBuilder();
linuxPath.Append("/mnt/");
linuxPath.Append(windowsPath[0].ToString().ToLower());
var parts = SH.Split(windowsPath, AllStrings.bs);
for (int i = 1; i < parts.Count; i++)
{
linuxPath.Append("/" + parts[i]);
}
command = "wsl";
arguments = " bash -c \"github-linguist '" + linuxPath + "'\"";
W32.EnableWow64FSRedirection(false);
// empty
var pc = PowershellRunner.InvokeSingle("cmd /c bash -c 'ruby -v'");
// empty
var pc2 = PowershellRunner.InvokeSingle("wsl bash -c whoami");
// with content
var pc3 = PowershellRunner.InvokeSingle("dir");
//command = #"c:\Windows\System32\wsl.exe";
//arguments = " -- github-linguist \"/mnt/d/Documents/Visual Studio 2017/Projects/LearnCss/LearnCss/Program.cs\"";
// empty
var pc4 = PowershellRunner.InvokeSingle(command + arguments);
PowershellRunner.InvokeProcess(command + ".exe", arguments);
var lines = PowershellRunner.InvokeSingle(command);
var line = CA.ReturnWhichContains(lines, lang).FirstOrNull();
if (line == null)
{
return null;
}
var result = line.ToString().Replace(lang, string.Empty).Trim();
return result;
}
Why calling with PowerShell class is not working?
I am currently trying to generate a CSV using nhibernate. This error does not occur on my development enviroment but it does on the live site that it's being used on. I have tried fiddling with time out's but this does not seem to have any effect as it's timing out way before it should. The timing is completely random, sometimes it'll be 3 seconds before it times out the next it will be 10 seconds. There doesn't seem to be any real consistancy in the timing.
Stack Trace:
System.Web.HttpException: The remote host closed the connection. The error code is 0x800703E3.
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush)
at Reports.CustomCSVWriter.WritetoHttpStream(String filename, Boolean header)
The code is as follows:
public class ProductSpreadSheetDownload : CustomCSVWriter
{
protected override string[] GetCollection()
{
Page.Server.ScriptTimeout = 300;
IList<Product> products = new List<Product>();
IStockScheme stockScheme = Fabric.ObjectProvider.Get<IStockScheme>();
ICriteria criteria = CoreHttpModule.Session.CreateCriteria(typeof(Product))
.Add(NHibernate.Expression.Expression.IsNotNull(Product.STOCK_CODE))
.Add(NHibernate.Expression.Expression.Eq(Product.IS_VISIBLE_ON_WEBSITE, true))
.Add(NHibernate.Expression.Expression.Eq(Product.STOCK_TYPE, StockType.StockItem))
.Add(NHibernate.Expression.Expression.Not(NHibernate.Expression.Expression.Like(Product.NAME, "%*%")));
AddCustomCriteria(criteria);
products = criteria.List<Product>();
products = Product.RemoveOrphanedAndUnrooted((List<Product>)products);
Product[] productArray = new Product[products.Count];
products.CopyTo(productArray, 0);
double?[] levels = stockScheme.GetStock(productArray, false);
List<string> productStringList = new List<string>();
IProductMapper mapper = Fabric.ObjectProvider.Get<IProductMapper>();
var rootUrl = Fabric.SettingsProvider.ReadSetting<string>("RootUrl", string.Empty);
string showOutOfStock = Page.Request.QueryString["ShowOutOfStock"];
int minStockLevel = int.MinValue;
if (showOutOfStock == "False")
minStockLevel = 0;
for (int i = 0; i < productArray.Length; i++)
{
if (levels[i] > minStockLevel && levels[i] != null && productArray[i].Parent != null && productArray[i].Parent.IsVisibleOnWebsite)
{
StringBuilder productStringBuilder = new StringBuilder();
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].Name));
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].StockCode));
productStringBuilder.AppendFormat("{0}, ", levels[i]);
productStringBuilder.AppendFormat("{0}, ", mapper.GetUrl(productArray[i]) );
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].Category));
productStringBuilder.AppendFormat("{0}, ", CleanString(productArray[i].SubCategory));
productStringBuilder.AppendFormat("{0}, ", CleanString(mapper.GetText(productArray[i], "Description")));
productStringBuilder.AppendFormat("{0}, ", mapper.GetImageUrl(productArray[i], "Main"));
AddCustomFields(productStringBuilder, mapper);
productStringList.Add(productStringBuilder.ToString().Trim().TrimEnd(','));
}
}
string[] productstrings = new string[productStringList.Count];
productStringList.CopyTo(productstrings, 0);
return productstrings;
}
/// <summary>
/// Override this method to add custom criteria to the feed
/// </summary>
/// <example>
/// criteria.Add(NHibernate.Expression.Expression.Eq(Product.IS_VISIBLE_ON_WEBSITE, true));
/// </example>
protected virtual void AddCustomCriteria(ICriteria criteria) {}
/// <summary>
/// Override this method to add custom fields to the CSV output
/// </summary>
/// <example>
/// productStringBuilder.AppendFormat("{0}, ", mapper.GetImageUrl(productArray[i], "Main"));
/// </example>
protected virtual void AddCustomFields(StringBuilder productStringBuilder, IProductMapper mapper) { }
protected override string Headers()
{
string headers = "Name, Stockcode, Stock_Level, URL, Category, SubCategory, Description, Main Image URL";
return headers;
}
/// <summary>
/// Removes characters that are not safe in a CSV file.
/// </summary>
protected static string CleanString(string stringToClean)
{
return string.IsNullOrEmpty(stringToClean) ? string.Empty : stringToClean.Replace("\n", " ").Replace(',', ' ');
}
}
}
Here's what's going on. I have an ASP.NET MVC 4 Web API web application. I can call API resources via URL. One of these functions get performance monitoring data for a specified amount of time and returns it in JSON once it has completed. However, what I want to do is return
It is IMPORTANT to note that I am working with a the browser and API resources in the model, not with a View. Please don't casually tell me to use Javascript in a View, because there is no view, or tell me to look at the SignalR wiki because the information for ".NET" sections is meant for desktop applications, not web apps. For example, you can't "Console.WriteLine()" to a browser.
To reiterate, I am using ASP.NET MVC 4 Web API to develop an API, and am calling the API via URL in the browser and it is returning JSON. I am attempting to use SignalR to have the app send JSON to the browser, but it is not doing anything at all. Rather, the application simply returns the completed JSON from the controller action with all of the performance data values once the process has completed. In other words, SignalR is not working.
So what I'm trying to do is while the API resource is gathering all the information, SignalR sends JSON to the browser every second so that the client can see what's going on in real time.
What I need to find out is why SignalR isn't sending it, and how I can send information to be displayed in the browser without Javascript, since I'm working from a model class, not from a view.
As you can see, I subscribe to the event using On, and then use Invoke to call the server-side hub method SendToClient.
Please let me know if I'm trying to do is impossible. I have never heard of a "real-time", dynamic API call via URL.
Here is my hub class. It is located in ~/signalr/hubs and is in a file called LiveHub.cs. The method Send is what I am trying to invoke in the method seen in the next code block.
namespace PerfMon2.signalr.hubs
{
public class LiveHub : Hub
{
public void SendToClient(List<DataValueInfo> json)
{
Clients.showValue(json);
}
}
}
Here is the method from LogDBRepository.cs that includes the SignalR calls.
public List<LogInfo> LogTimedPerfData(string macName, string categoryName, string counterName,
string instanceName, string logName, string live, long? seconds)
{
iModsDBRepository modsDB = new iModsDBRepository();
List<MachineInfo> theMac = modsDB.GetMachineByName(macName);
if (theMac.Count == 0)
return new List<LogInfo>();
else if (instanceName == null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) )
{
return new List<LogInfo>();
}
}
else if (instanceName != null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) ||
!PerformanceCounterCategory.InstanceExists(instanceName, categoryName, macName))
{
return new List<LogInfo>();
}
}
else if (logName == null)
{
return new List<LogInfo>();
}
// Check if entered log name is a duplicate for the authenticated user
List<LogInfo> checkDuplicateLog = this.GetSingleLog(logName);
if (checkDuplicateLog.Count > 0)
{
return new List<LogInfo>();
}
PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName, theMac[0].MachineName);
if (category.CategoryName == null || category.MachineName == null)
{
return new List<LogInfo>();
}
List<LogInfo> logIt = new List<LogInfo>();
if (category.CategoryType != PerformanceCounterCategoryType.SingleInstance)
{
List<InstanceInfo> instances = modsDB.GetInstancesFromCatMacName(theMac[0].MachineName, category.CategoryName);
foreach (InstanceInfo inst in instances)
{
if (!category.InstanceExists(inst.InstanceName))
{
continue;
}
else if (inst.InstanceName.Equals(instanceName, StringComparison.OrdinalIgnoreCase))
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
inst.InstanceName, theMac[0].MachineName);
//CounterSample data = perfCounter.NextSample();
//double value = CounterSample.Calculate(data, perfCounter.NextSample());
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
break;
}
}
}
else
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
"", theMac[0].MachineName);
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
}
return logIt;
}
Here is the controller for the method in LogController.cs :
[AcceptVerbs("GET", "POST")]
public List<LogInfo> Log_Perf_Data(string machine_name, string category_name, string counter_name, string instance_name,
string log_name, long? seconds, string live, string enforceQuery)
{
LogController.CheckUser();
// POST api/log/post_data?machine_name=&category_name=&counter_name=&instance_name=&log_name=&seconds=
if (machine_name != null && category_name != null && counter_name != null && log_name != null && seconds.HasValue && enforceQuery == null)
{
List<LogInfo> dataVal = logDB.LogTimedPerfData(machine_name, category_name, counter_name, instance_name,
log_name, live, seconds);
logDB.SaveChanges();
return dataVal;
}
return new List<LogInfo>();
}
Maybe you can implement it in push technique. Here is how I do it:
Class with message
public class Message
{
/// <summary>
/// The name who will receive this message.
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// The message content.
/// </summary>
public string MessageContent { get; set; }
}
Class that will represent client:
public class Client
{
private ManualResetEvent messageEvent = new ManualResetEvent(false);
private Queue<Message> messageQueue = new Queue<Message>();
/// <summary>
/// This method is called by a sender to send a message to this client.
/// </summary>
/// <param name="message">the new message</param>
public void EnqueueMessage(Message message)
{
lock (messageQueue)
{
messageQueue.Enqueue(message);
// Set a new message event.
messageEvent.Set();
}
}
/// <summary>
/// This method is called by the client to receive messages from the message queue.
/// If no message, it will wait until a new message is inserted.
/// </summary>
/// <returns>the unread message</returns>
public Message DequeueMessage()
{
// Wait until a new message.
messageEvent.WaitOne();
lock (messageQueue)
{
if (messageQueue.Count == 1)
{
messageEvent.Reset();
}
return messageQueue.Dequeue();
}
}
}
Class to send messages to clients:
public class ClientAdapter
{
/// <summary>
/// The recipient list.
/// </summary>
private Dictionary<string, Client> recipients = new Dictionary<string,Client>();
/// <summary>
/// Send a message to a particular recipient.
/// </summary>
public void SendMessage(Message message)
{
if (recipients.ContainsKey(message.RecipientName))
{
Client client = recipients[message.RecipientName];
client.EnqueueMessage(message);
}
}
/// <summary>
/// Called by a individual recipient to wait and receive a message.
/// </summary>
/// <returns>The message content</returns>
public string GetMessage(string userName)
{
string messageContent = string.Empty;
if (recipients.ContainsKey(userName))
{
Client client = recipients[userName];
messageContent = client.DequeueMessage().MessageContent;
}
return messageContent;
}
/// <summary>
/// Join a user to the recipient list.
/// </summary>
public void Join(string userName)
{
recipients[userName] = new Client();
}
/// <summary>
/// Singleton pattern.
/// This pattern will ensure there is only one instance of this class in the system.
/// </summary>
public static ClientAdapter Instance = new ClientAdapter();
private ClientAdapter() { }
}
Sending messages:
Message message = new Message
{
RecipientName = tbRecipientName.Text.Trim(),
MessageContent = tbMessageContent.Text.Trim()
};
if (!string.IsNullOrWhiteSpace(message.RecipientName) && !string.IsNullOrEmpty(message.MessageContent))
{
// Call the client adapter to send the message to the particular recipient instantly.
ClientAdapter.Instance.SendMessage(message);
}
Receive messages (this is JavaScript functions written in test page. They render content of the message on ASPX page. Here you should implement your logic):
// This method will persist a http request and wait for messages.
function waitEvent() {
CSASPNETReverseAJAX.Dispatcher.WaitMessage("<%= Session["userName"] %>",
function (result) {
displayMessage(result);
// Keep looping.
setTimeout(waitEvent, 0);
}, function () {
// Keep looping.
setTimeout(waitEvent, 0);
});
}
// Append a message content to the result panel.
function displayMessage(message) {
var panel = document.getElementById("<%= lbMessages.ClientID %>");
panel.innerHTML += currentTime() + ": " + message + "<br />";
}
// Return a current time string.
function currentTime() {
var currentDate = new Date();
return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
}
How to remove a query string by Key from a Url?
I have the below method which works fine but just wondering is there any better/shorter way? or a built-in .NET method which can do it more efficiently?
public static string RemoveQueryStringByKey(string url, string key)
{
var indexOfQuestionMark = url.IndexOf("?");
if (indexOfQuestionMark == -1)
{
return url;
}
var result = url.Substring(0, indexOfQuestionMark);
var queryStrings = url.Substring(indexOfQuestionMark + 1);
var queryStringParts = queryStrings.Split(new [] {'&'});
var isFirstAdded = false;
for (int index = 0; index <queryStringParts.Length; index++)
{
var keyValue = queryStringParts[index].Split(new char[] { '=' });
if (keyValue[0] == key)
{
continue;
}
if (!isFirstAdded)
{
result += "?";
isFirstAdded = true;
}
else
{
result += "&";
}
result += queryStringParts[index];
}
return result;
}
For example I can call it like:
Console.WriteLine(RemoveQueryStringByKey(#"http://www.domain.com/uk_pa/PostDetail.aspx?hello=hi&xpid=4578", "xpid"));
Hope the question is clear.
Thanks,
This works well:
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url);
// this gets all the query string key value pairs as a collection
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
// this removes the key if exists
newQueryString.Remove(key);
// this gets the page path from root without QueryString
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
return newQueryString.Count > 0
? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString)
: pagePathWithoutQueryString;
}
an example:
RemoveQueryStringByKey("https://www.google.co.uk/search?#hl=en&output=search&sclient=psy-ab&q=cookie", "q");
and returns:
https://www.google.co.uk/search?#hl=en&output=search&sclient=psy-ab
var queryString = "hello=hi&xpid=4578";
var qs = System.Web.HttpUtility.ParseQueryString(queryString);
qs.Remove("xpid");
var newQuerystring = qs.ToString();
This still works in .NET 5.
There's a useful class called UriBuilder in the System namespace. We can use it along with a couple of extension methods to do the following:
Uri u = new Uri("http://example.com?key1=value1&key2=value2");
u = u.DropQueryItem("key1");
Or like this:
Uri u = new Uri("http://example.com?key1=value1&key2=value2");
UriBuilder b = new UriBuilder(u);
b.RemoveQueryItem("key1");
u = b.Uri;
The extension methods:
using System;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
public static class UriExtensions
{
public static Uri DropQueryItem(this Uri u, string key)
{
UriBuilder b = new UriBuilder(u);
b.RemoveQueryItem(key);
return b.Uri;
}
}
public static class UriBuilderExtensions
{
private static string _ParseQueryPattern = #"(?<key>[^&=]+)={0,1}(?<value>[^&]*)";
private static Regex _ParseQueryRegex = null;
private static Regex ParseQueryRegex
{
get
{
if (_ParseQueryRegex == null)
{
_ParseQueryRegex = new Regex(_ParseQueryPattern, RegexOptions.Compiled | RegexOptions.Singleline);
}
return _ParseQueryRegex;
}
}
public static void SetQueryItem(this UriBuilder b, string key, string value)
{
NameValueCollection parms = ParseQueryString(b.Query);
parms[key] = value;
b.Query = RenderQuery(parms);
}
public static void RemoveQueryItem(this UriBuilder b, string key)
{
NameValueCollection parms = ParseQueryString(b.Query);
parms.Remove(key);
b.Query = RenderQuery(parms);
}
private static string RenderQuery(NameValueCollection parms)
{
StringBuilder sb = new StringBuilder();
for (int i=0; i<parms.Count; i++)
{
string key = parms.Keys[i];
sb.Append(key + "=" + parms[key]);
if (i < parms.Count - 1)
{
sb.Append("&");
}
}
return sb.ToString();
}
public static NameValueCollection ParseQueryString(string query, bool caseSensitive = true)
{
NameValueCollection pairs = new NameValueCollection(caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);
string q = query.Trim().TrimStart(new char[] {'?'});
MatchCollection matches = ParseQueryRegex.Matches(q);
foreach (Match m in matches)
{
string key = m.Groups["key"].Value;
string value = m.Groups["value"].Value;
if (pairs[key] != null)
{
pairs[key] = pairs[key] + "," + value;
}
else
{
pairs[key] = value;
}
}
return pairs;
}
}
I know this is a rather old question, but everything I read felt a bit complicated.
public Uri GetUriWithoutQueryParam( Uri originalUri, string paramKey ) {
NameValueCollection newQuery = HttpUtility.ParseQueryString( originalUri.Query );
newQuery.Remove( paramKey );
return new UriBuilder( originalUri ) { Query = newQuery.ToString() }.Uri;
}
We can also do it using regex
string queryString = "Default.aspx?Agent=10&Language=2"; //Request.QueryString.ToString();
string parameterToRemove="Language"; //parameter which we want to remove
string regex=string.Format("(&{0}=[^&\s]+|(?<=\?){0}=[^&\s]+&?)",parameterToRemove); //this will not work for javascript, for javascript you can do following
string finalQS = Regex.Replace(queryString, regex, "");
//javascript(following is not js syntex, just want to give idea how we can able do it in js)
string regex1 = string.Format("(&{0}=[^&\s]+)",parameterToRemove);
string regex2 = string.Format("(\?{0}=[^&\s]+&?)",parameterToRemove);
string finalQS = Regex.Replace(queryString, regex1, "").Replace(queryString, regex2, "");
https://regexr.com/3i9vj
How about this:
string RemoveQueryStringByKey(string url, string key)
{
string ret = string.Empty;
int index = url.IndexOf(key);
if (index > -1)
{
string post = string.Empty;
// Find end of key's value
int endIndex = url.IndexOf('&', index);
if (endIndex != -1) // Last query string value?
{
post = url.Substring(endIndex, url.Length - endIndex);
}
// Decrement for ? or & character
--index;
ret = url.Substring(0, index) + post;
}
return ret;
}
I found a way without using Regex:
private string RemoveQueryStringByKey(string sURL, string sKey) {
string sOutput = string.Empty;
int iQuestion = sURL.IndexOf('?');
if (iQuestion == -1) return (sURL);
int iKey = sURL.Substring(iQuestion).IndexOf(sKey) + iQuestion;
if (iKey == -1) return (sURL);
int iNextAnd = sURL.Substring(iKey).IndexOf('&') + iKey + 1;
if (iNextAnd == -1) {
sOutput = sURL.Substring(0, iKey - 1);
}
else {
sOutput = sURL.Remove(iKey, iNextAnd - iKey);
}
return (sOutput);
}
I did try this with adding another field at the end, and it works fine for that too.
I'm thinking the shortest way (that I believe produces a valid URL in all cases, assuming the URL was valid to begin with) would be to use this regex (where getRidOf is the variable name you are trying to remove) and the replacement is a zero-length string ""):
(?<=[?&])getRidOf=[^&]*(&|$)
or maybe even
\bgetRidOf=[^&]*(&|$)
while possibly not the absolute prettiest URLs, I think they are all valid:
INPUT OUTPUT
----------- ------------
blah.com/blah.php?getRidOf=d.co&blah=foo blah.com/blah.php?blah=foo
blah.com/blah.php?f=0&getRidOf=d.co&blah=foo blah.com/blah.php?f=0&blah=foo
blah.com/blah.php?hello=true&getRidOf=d.co blah.com/blah.php?hello=true&
blah.com/blah.php?getRidOf=d.co blah.com/blah.php?
and it's a simple regex replace:
Dim RegexObj as Regex = New Regex("(?<=[?&])getRidOf=[^&]*(&|$)")
RegexObj.Replace("source.url.com/find.htm?replace=true&getRidOf=PLEASE!!!", "")
...should result in the string:
"source.url.com/find.htm?replace=true&"
...which seems to be valid for an ASP.Net application, while replace does equal true (not true& or anything like that)
I'll try to adapt it if you have a case where it won't work :)
public static string RemoveQueryStringByKey(string sURL, string sKey)
{
string sOutput = string.Empty;
string sToReplace = string.Empty;
int iFindTheKey = sURL.IndexOf(sKey);
if (iFindTheKey == -1) return (sURL);
int iQuestion = sURL.IndexOf('?');
if (iQuestion == -1) return (sURL);
string sEverythingBehindQ = sURL.Substring(iQuestion);
List<string> everythingBehindQ = new List<string>(sEverythingBehindQ.Split('&'));
foreach (string OneParamPair in everythingBehindQ)
{
int iIsKeyInThisParamPair = OneParamPair.IndexOf(sKey);
if (iIsKeyInThisParamPair != -1)
{
sToReplace = "&" + OneParamPair;
}
}
sOutput = sURL.Replace(sToReplace, "");
return (sOutput);
}
Below code before deleting your QueryString.
PropertyInfo isreadonly =
typeof(System.Collections.Specialized.NameValueCollection).GetProperty(
"IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isreadonly.SetValue(this.Request.QueryString, false, null);
// remove
this.Request.QueryString.Remove("yourKey");
Sorry this is a bit dirty but should work in older framework
public String RemoveQueryString( String rawUrl , String keyName)
{
var currentURL_Split = rawUrl.Split('&').ToList();
currentURL_Split = currentURL_Split.Where(o => !o.ToLower().StartsWith(keyName.ToLower()+"=")).ToList();
String New_RemovedKey = String.Join("&", currentURL_Split.ToArray());
New_RemovedKey = New_RemovedKey.Replace("&&", "&");
return New_RemovedKey;
}
Here is my solution:
I'v added some extra input validation.
public static void TryRemoveQueryStringByKey(ref string url, string key)
{
if (string.IsNullOrEmpty(url) ||
string.IsNullOrEmpty(key) ||
Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
{
return false;
}
try
{
Uri uri = new Uri(url);
// This gets all the query string key value pairs as a collection
NameValueCollection queryCollection = HttpUtility.ParseQueryString(uri.Query);
string keyValue = queryCollection.Get(key);
if (url.IndexOf("&" + key + "=" + keyValue, StringComparison.OrdinalIgnoreCase) >= 0)
{
url = url.Replace("&" + key + "=" + keyValue, String.Empty);
return true;
}
else if (url.IndexOf("?" + key + "=" + keyValue, StringComparison.OrdinalIgnoreCase) >= 0)
{
url = url.Replace("?" + key + "=" + keyValue, String.Empty);
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
Some unit testing examples:
string url1 = "http://www.gmail.com?a=1&cookie=cookieValue"
Assert.IsTrue(TryRemoveQueryStringByKey(ref url1,"cookie")); //OUTPUT: "http://www.gmail.com?a=1"
string url2 = "http://www.gmail.com?cookie=cookieValue"
Assert.IsTrue(TryRemoveQueryStringByKey(ref url2,"cookie")); //OUTPUT: "http://www.gmail.com"
string url3 = "http://www.gmail.com?cookie="
Assert.IsTrue(TryRemoveQueryStringByKey(ref url2,"cookie")); //OUTPUT: "http://www.gmail.com"
Here's a full solution that works with >= 0 params specified, and any form of URL:
/// <summary>
/// Given a URL in any format, return URL with specified query string param removed if it exists
/// </summary>
public static string StripQueryStringParam(string url, string paramToRemove)
{
return StripQueryStringParams(url, new List<string> {paramToRemove});
}
/// <summary>
/// Given a URL in any format, return URL with specified query string params removed if it exists
/// </summary>
public static string StripQueryStringParams(string url, List<string> paramsToRemove)
{
if (paramsToRemove == null || !paramsToRemove.Any()) return url;
var splitUrl = url.Split('?');
if (splitUrl.Length == 1) return url;
var urlFirstPart = splitUrl[0];
var urlSecondPart = splitUrl[1];
// Even though in most cases # isn't available to context,
// we may be passing it in explicitly for helper urls
var secondPartSplit = urlSecondPart.Split('#');
var querystring = secondPartSplit[0];
var hashUrlPart = string.Empty;
if (secondPartSplit.Length > 1)
{
hashUrlPart = "#" + secondPartSplit[1];
}
var nvc = HttpUtility.ParseQueryString(querystring);
if (!nvc.HasKeys()) return url;
// Remove any matches
foreach (var key in nvc.AllKeys)
{
if (paramsToRemove.Contains(key))
{
nvc.Remove(key);
}
}
if (!nvc.HasKeys()) return urlFirstPart;
return urlFirstPart +
"?" + string.Join("&", nvc.AllKeys.Select(c => c.ToString() + "=" + nvc[c.ToString()])) +
hashUrlPart;
}
A more modern answer for this old question in case someone else stumbles across it like I did.
This is using the Uri class to parse the URL (can be skipped if your URL is already in a Uri object) and LINQ to filter the query string.
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url, UriKind.Absolute);
var queryParts = uri.Query
.TrimStart('?')
.Split('&')
.Where(item => string.CompareOrdinal(item, key) != 0);
return uri.Scheme + Uri.SchemeDelimiter
+ uri.Authority
+ uri.AbsolutePath
+ "?" + string.Join("&", queryParts);
}
With reusing the signature from the accepted answer, but preserving the fragment and using QueryHelpers from Microsoft.AspNetCore.WebUtilities.
public static string RemoveQueryStringByKey(string url, string key)
{
var uri = new Uri(url);
var newQueryString = QueryHelpers.ParseQuery(uri.Query);
if (newQueryString.Remove(key))
{
var urlWithNewQuery = QueryHelpers.AddQueryString(
uri.GetLeftPart(UriPartial.Path),
newQueryString.ToDictionary(
queryParam => queryParam.Key,
queryParam => queryParam.Value.ToString()))
return $"{urlWithNewQuery}{uri.Fragment}";
}
return url;
}
string url = HttpContext.Current.Request.Url.AbsoluteUri;
string[] separateURL = url.Split('?');
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(separateURL[1]);
queryString.Remove("param_toremove");
string revisedurl = separateURL[0] + "?" + queryString.ToString();