Using identical query params with QueryHelpers - c#

I'm trying to generate an url like,
www.example.com/Example?someOption=true&anotherOption=false&filter=testFilter&filter=testFilter2&filter=testFilter3
I have been using StringBuilder so far for the task but I'd like to think it's not the appropriate way of doing it. As a result, I came to conclusion that I should be generating this link using Uri class and not StringBuilder or any string extensions. Soon after, I came across QueryHelpers.AddQueryString. The issue with that is, it's using Dictionaries to add query parameters and adding an identical parameter ('filter' in my example) is just not possible.
Just wondering is there any other built in function or library that I can use to cleanly generate my urls with identical query parameters?

One of the overloads to QueryHelpers.AddQueryString takes an enumerable of key/value pairs. This means you can create your own 'dictionary' like this:
var filters = new List<KeyValuePair<string, string>>
{
new KeyValuePair("filter","testFilter1"),
new KeyValuePair("filter","testFilter2"),
new KeyValuePair("filter","testFilter3"),
}
Then you can call
var uri = QueryHelpers.AddQueryString("www.example.com", filters);

A little more elegant:
public async Task<HttpResponseMessage> GetAsync(string url, Action<HttpRequestHeaders> headers, Action<HttpRequestParameters> parameters)
{
using (var client = new HttpClient())
{
var _httpRequestParameters = new HttpRequestParameters();
headers?.Invoke(client.DefaultRequestHeaders);
if (parameters != null)
{
parameters.Invoke(_httpRequestParameters);
var query = _httpRequestParameters.GetQueryString();
url += query;
}
return await client.GetAsync(url);
}
}

Related

Can I add a list or array of query strings to uribuilder?

I'm using UriBuilder to create a url for an API endpoint.
I need to add some query strings for it, and I can do that nicely with the following example:
private async Task<string> CallAPI(string url, string queryString)
{
string s = "https://mysite.wendesk.com/api/v2/search/export/";
UriBuilder uriBuild = new UriBuilder(s);
uriBuild.Query = queryString;
using (var result = await _HttpClient.GetAsync($"{uriBuild.Uri.ToString()}"))
{
if (!result.IsSuccessStatusCode)
{
throw new Exception("bad status code from zendesk");
}
return await result.Content.ReadAsStringAsync();
}
}
Which is easy and nice. But I need to a quite a few query strings, and depending on who's calling the function, I need varying amounts.
So another solution could be something like this:
private async Task<string> CallAPI(string url, string[] queryStrings)
{
string s = "https://mysite.wendesk.com/api/v2/search/export/";
UriBuilder uriBuild = new UriBuilder(s);
uriBuild.Query = string.Join("&", queryStrings);
using (var result = await _HttpClient.GetAsync($"{uriBuild.Uri.ToString()}"))
{
if (!result.IsSuccessStatusCode)
{
throw new Exception("bad status code from zendesk");
}
return await result.Content.ReadAsStringAsync();
}
}
But I was wondering if there was something that could feel more "native". Perhaps something like creating a dicitionary with keys and values, so that the caller could just create a dictionary instead of hardcoding so much of the query strings in there?
I think NameValueCollection might work for a solution like you mentioned. You can use a dynamically method.
For example:
private Task<string> CreateQuery(NameValueCollection nvc)
{
var values = from key in nvc.AllKeys
from value in nvc.GetValues(key)
select string.Format(
"{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value));
return Task.FromResult("?" + Join("&", values));
}

Adding multiple Healthchecks in .net 6 with custom IHealthCheck object

For my new requirement, I will have list of URLs(from configuration) and the number of URLs might be more then 10.I need to add IHealthCheck instances for each URL to have health status of all the URLs.
Single URL I can add like below, But how to have Healthcheck for all the URLs
builder.Services.AddHealthChecks().AddCheck<UnknownURIHealthCheck>("ExternalHealthCheck");
IHealthCheck implementation is like
public class UnknownURIHealthCheck : IHealthCheck
{
private readonly int _uri;
public UnknownURIHealthCheck(int uri)
{
_uri = uri;
}
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
if (_uri < 10)
return Task.FromResult(HealthCheckResult.Healthy($"URI Value {_uri}"));
else
return Task.FromResult(HealthCheckResult.Unhealthy($"URI Value {_uri}"));
}
How to create IHealthCheck instance for each URI? Am I trying/looking in a wrong way for my requirement. Can some one help me?
Note: Declared _uri as integer for the purpose of explaining, _uri<10 will be replaced with actual uri's availability logic.
Instead of using the generic version of AddCheck, you can use an overload that takes an instance of the health check as a parameter:
builder.Services
.AddHealthChecks()
.AddCheck(
"ExternalHealthCheck-1",
new UnknownURIHealthCheck("URL1"))
.AddCheck(
"ExternalHealthCheck-2",
new UnknownURIHealthCheck("URL2"));
If you want to add many health checks in a loop, you can use this approach:
var urls = new string[] { "URL1", "URL2" };
var bldr = builder.Services
.AddHealthChecks();
foreach (var url in urls)
{
bldr = bldr.AddCheck(
"ExternalHealthCheck-" + url,
new UnknownURIHealthCheck(url));
}

RestRequest Parameters adding logic

I am having some issues finding information about adding some logic field in my RestRequest using V 107. I am trying to add a filter to my GET query
dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'
There are a few other queries in the call which i am using Dictionary<string, string> to store them, and it works great however it only works if i am looking for something equal to, as adding it to the parameters it seems by default its equal to and i am not finding any way to add any other logic, gt/ge/lt/le etc. using the older version i would just append the url adding the logic i need, but i am not seeing a way to append the url either. Looking over their documentation i either missed it, cant find it, or its not there. Any help would be greatly appreciated! My method looks like this
public static async Task<string> GET_API(String RequestUrl, string RequestObject, Dictionary<string, string> parameters)
{
var request = new RestRequest(RequestObject);
var options = new RestClientOptions(RequestUrl)
{
ThrowOnAnyError = true,
Timeout = -1
};
var client = new RestClient(options);
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("Bearer " + TokenManager.GetAccessTokenString("TRN"));
foreach (var parameter in parameters)
{
request.AddQueryParameter(parameter.Key, parameter.Value);
}
var response = await client.GetAsync(request);
return response.Content.ToString();
}
I send the BaseURL , the RequestObject would be table i am calling in the base URL, and my dictionary item contains the Field name, and the field values that i am dynamically generating on another method that would append the string. and example would be
parameters.Add("dl_document_name", "TableA");
which would append the URL with dl_document_name eq 'TableA'
it would call the API after i add the OAuth Token i create and return the data i need and send it back. or another option i guess could be appending the string with the logic i need to return the data
You should use OData, it's easy to implement and it has different kind of filters, you also can set which filters are usable and which aren't.
https://www.odata.org/
I figured out a work around, if i only have one i can add it to the first parameter and adding the filter as the first key, which will work unless i have multiple conditions that are not eq
parameters.Add("filter","dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'");

QueryString.Add not adding value to the Request query strings in ASP.NET Core

I am writing a middleware where I want to modify query string values for the current request but I am unable to do that. afaik QueryString.Add method should work but it doesn't affect the query string. Here's what I tried.
public async Task Invoke(HttpContext context, IHeaderValue headerValue, IUser user)
{
var result = context.Request.Headers["Authorization"];
if (result.Count != 0)
{
headerValue.UserId = this.GetUserIdFromToken(result[0]);
var request = context.Request;
if (request.Method == "GET")
{
// It should be adding the new query value to the QueryString collection
// but it doesn't
request.QueryString.Add("CurrentUserId", headerValue.UserId.ToString());
}
}
}
I will really appreciate any help with this.
QueryString.Add returns a new QueryString containing the given name and value. It doesn't mutate the QueryString on which it's called.
So you need to do something like
request.QueryString = request.QueryString.Add("A", "B");

What is the ASP.NET Core MVC equivalent to Request.RequestURI?

I found a blog post that shows how to "shim" familiar things like HttpResponseMessage back into ASP.NET Core MVC, but I want to know what's the new native way to do the same thing as the following code in a REST Post method in a Controller:
// POST audit/values
[HttpPost]
public System.Net.Http.HttpResponseMessage Post([FromBody]string value)
{
var NewEntity = _repository.InsertFromString(value);
var msg = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Created);
msg.Headers.Location = new Uri(Request.RequestUri + NewEntity.ID.ToString());
return msg;
}
In an ASP.NET Core MVC project, I can't seem to get Request.RequestUri.
I tried inspecting Request, and I was able to make a function like this:
private string UriStr(HttpRequest Request)
{
return Request.Scheme + "://" + Request.Host + Request.Path; // Request.Path has leading /
}
So I could write UriStr(Request) instead. But I'm not sure that's right. I feel like I'm hacking my way around, and not using this correctly.
A related question for earlier non-Core ASP.NET MVC versions asks how to get the base url of the site.
Personally, I use :
new Uri(request.GetDisplayUrl())
GetDisplayUrl fully un-escaped form (except for the QueryString)
GetEncodedUrl - fully escaped form suitable for use in HTTP headers
These are extension method from the following namespace : Microsoft.AspNetCore.Http.Extensions
A cleaner way would be to use a UriBuilder:
private static Uri GetUri(HttpRequest request)
{
var builder = new UriBuilder();
builder.Scheme = request.Scheme;
builder.Host = request.Host.Value;
builder.Path = request.Path;
builder.Query = request.QueryString.ToUriComponent();
return builder.Uri;
}
(not tested, the code might require a few adjustments)
Here's a working code. This is based off #Thomas Levesque answer which didn't work well when the request is from a custom port.
public static class HttpRequestExtensions
{
public static Uri ToUri(this HttpRequest request)
{
var hostComponents = request.Host.ToUriComponent().Split(':');
var builder = new UriBuilder
{
Scheme = request.Scheme,
Host = hostComponents[0],
Path = request.Path,
Query = request.QueryString.ToUriComponent()
};
if (hostComponents.Length == 2)
{
builder.Port = Convert.ToInt32(hostComponents[1]);
}
return builder.Uri;
}
}

Categories

Resources