How to create Uri without fragmentation (convert # to %23) - c#

I am trying to do advanced searches by using the query string but when I include # it doesn't get converted to %23 when creating a uri.
var webAddress = "www.worldwideweb.com/abc#d#e";
var uri = new Uri(webAddress).AbsoluteUri;
When I do that, an exception is thrown.
When I only include a # symbol it fragments it instead. Like in this example
var webAddress = "https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [java]"
var uri = new Uri(webAddress).AbsoluteUri;
Uri now equals
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#]%20OR%20[java]
How do I make
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [f#]
into
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c%23]%20OR%20[f%23]
I'm using .Net Framework 4.6.2

My approach is to use the an UriBuilder and a Dictionary for each Query parameter. You can then UrlEncode the value from each parameter so you get a valid Url.
This is what your code would look like:
var ub = new UriBuilder("https", "api.stackexchange.com");
ub.Path = "/2.2/search/advanced";
// query string parameters
var query = new Dictionary<string,string> ();
query.Add("site", "stackoverflow");
query.Add("q", "[c#] OR [f#]");
query.Add("filter", "!.UE46gEJXV)W0GSb");
query.Add("page","1");
query.Add("pagesize","2");
// iterate over each dictionary item and UrlEncode the value
ub.Query = String.Join("&",
query.Select(kv => kv.Key + "=" + WebUtility.UrlEncode(kv.Value)));
var wc = new MyWebClient();
wc.DownloadString(ub.Uri.AbsoluteUri).Dump("result");
This will result in this Url in ub.Uri.AbsoluteUri:
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=%5Bc%23%5D+OR+%5Bf%23%5D&filter=!.UE46gEJXV)W0GSb&page=1&pagesize=2
As the StackAPI returns the content zipped, use AutomaticDecompression on an subclassed WebClient (as shown here by feroze):
class MyWebClient:WebClient
{
protected override WebRequest GetWebRequest(Uri uri)
{
var wr = base.GetWebRequest(uri) as HttpWebRequest;
wr.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
return wr;
}
}
which, when combined with the other code, generates output for me:
{
"items" : [{
"tags" : ["c#", "asp.net-mvc", "iis", "web-config"],
"last_activity_date" : 1503056272,
"question_id" : 45712096,
"link" : "https://stackoverflow.com/questions/45712096/can-not-read-web-config-file",
"title" : "Can not read web.config file"
}, {
"tags" : ["c#", "xaml", "uwp", "narrator"],
"last_activity_date" : 1503056264,
"question_id" : 45753140,
"link" : "https://stackoverflow.com/questions/45753140/narrator-scan-mode-for-textblock-the-narrator-reads-the-text-properties-twice",
"title" : "Narrator. Scan mode. For TextBlock the narrator reads the Text properties twice"
}
]
}

If # only exists in query part, you can simply do this:
var webAddress = "https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [java]"
var uri = new Uri(webAddress).AbsoluteUri;
var fixedUri = Regex.Replace(uri, "#", "%23");

Related

Property or indexer 'AuthenticationHeaderValue.Parameter' cannot be assigned to -- it is read only

public IList<FormResponse> GetForms(HttpRequestMessage request)
{
string storeCode = ExtractBasicAuthUserFromHeader(request);
List<Form> forms = _apiRepository.GetForms(storeCode); ///23424324
return forms;
}
private string ExtractBasicAuthUserFromHeader(HttpRequestMessage reqeust)
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(reqeust.Headers.Authorization.Parameter
));
return usernamePassword.Substring(0, usernamePassword.IndexOf(':'));
}
I wrote following test to test above
private readonly Mock<IApiRepository> _apiRepository = new Mock<IApiRepository>();
[TestInitialize]
public void Init()
{
_apiRepository.Setup(x => x.GetForms("23424324")).Returns(_forms); //skipping FromBase64String converation for understanding
}
[TestMethod]
public void GetForms_ReturnFormList()
{
HttpRequestMessage reqeust = new HttpRequestMessage(); ;
//Error CS0200 Property or indexer 'AuthenticationHeaderValue.Parameter' cannot be assigned to -- it is read only
reqeust.Headers.Authorization.Parameter = "23424324:12341234123";
IList<FormResponse> formList = _formService.GetForms(reqeust);
Assert.AreEqual(formList.Count, 2);
}
getting following error
Error CS0200 Property or indexer 'AuthenticationHeaderValue.Parameter' cannot be assigned to -- it is read only
wondering how can I mock HttpRequestMessage reqeust = new HttpRequestMessage();
to add reqeust.Headers.Authorization.Parameter base string
to test GetForms functionality in services class
request.Headers.Authorization is of type AuthenticationHeaderValue and this class has properties Scheme and Parameter. These properties are ready-only.
You are getting error because you are trying to assign value to these ready-only properties.
What you need to do is to assign value to request.Headers.Authorization by creating an object of AuthenticationHeaderValue class.
Consider doing following.
request.Headers.Authorization = new AuthenticationHeaderValue(somescheme, someparameter);
You need to put actual values in place of somescheme and someparameter in above code.
You can get details about HttpRequestMessage and AuthenticationHeaderValue classes at below mentioned links.
https://msdn.microsoft.com/en-us/library/system.net.http.headers.authenticationheadervalue(v=vs.118).aspx
https://msdn.microsoft.com/en-us/library/system.net.http.httprequestmessage(v=vs.118).aspx
https://msdn.microsoft.com/en-us/library/system.net.http.headers.httprequestheaders(v=vs.118).aspx
You can use something like this:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("key", "=" + value);
More here:
Setting Authorization Header of HttpClient

C# How to use Uri equal?

Some Test:
This is Ture:
[Fact]
public void UriEqualTest()
{
//Act
var uri1 = new Uri("https://www.baidu.com");
var uri2 = new Uri("https://www.baidu.com/");
var boolResult = uri2.Equals(uri1);
//Assert
Assert.Equal(uri1, uri2);
Assert.True(boolResult);//True
}
This is Ture:
[Fact]
public void UriUpperEqualTest()
{
//Act
var uri1 = new Uri("https://wWw.bAidu.com");
var uri2 = new Uri("https://www.baidu.com/");
var boolResult = uri2.Equals(uri1);
var operatorResult = (uri1 == uri2);
//Assert
Assert.Equal(uri1, uri2);
Assert.True(boolResult);//True
}
This is False:
[Fact]
public void UrlEqualTest()
{
//Act
var uri1 = new Uri("https://www.baidu.com/aaaa/bbbb");
var uri2 = new Uri("https://www.baidu.com/aaaa/bbbb/");
var boolResult = uri2.Equals(uri1);
//Assert
Assert.Equal(uri1, uri2);
Assert.True(boolResult);//False
}
This is False:
[Fact]
public void UrlUpperEqualTest()
{
//Act
var uri1 = new Uri("https://www.baidu.com/AAaa/bbbb");
var uri2 = new Uri("https://www.baidu.com/aAAa/bbbb");
var boolResult = uri2.Equals(uri1);
var operatorResult = (uri1 == uri2);
//Assert
Assert.Equal(uri1, uri2);
Assert.True(boolResult);//False
}
This is True:
[Fact]
public void UriUpperEqualAndPathTest()
{
//Act
var uri1 = new Uri("https://www.baiDu.com/aaaa/bbbb");
var uri2 = new Uri("https://www.Baidu.com/aaaa/bbbb");
var boolResult = uri2.Equals(uri1);
//Assert
Assert.Equal(uri1, uri2);
Assert.True(boolResult);//True
}
So,The Host not case sensitive? but path case sensitive??
And I want all Uri dot not case sensitive and dot not case '/',What should I do?
And I want all Uri dot not case sensitive and dot not case '/',What should I do?
And I want all Uri dot not case sensitive and dot not case '/',What should I do?
And in aspnet core mvc, if i use route
[HttpGet("/private/head")] and [HttpGet("/private/HeAd")] and [HttpGet("/private/head/")]
It's error! the error is:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
https://stackoverflow.com/a/2581418/34092 states:
As far as the protocol is concerned, http://example.com/something and
http://example.com/something/ are quite different. Some servers might
redirect you from one to the other if it is implemented in such a way.
As for the pure domain names, it always sends a request ending with a
slash. (The domain name itself is not included in the path section of
an HTTP request, just as Greg Hewgill and the others wrote. It is,
however, included in the headers.)
So, looking at your examples:
var uri1 = new Uri("https://www.baidu.com");
var uri2 = new Uri("https://www.baidu.com/");
They are the same, since always sends a request ending with a slash. They are thus equivalent.
https://serverfault.com/a/261344 states:
Names resolved from DNS are case insensitive. This is important to
prevent confusion.
var uri1 = new Uri("https://wWw.bAidu.com");
var uri2 = new Uri("https://www.baidu.com/");
Thus, the two are equivalent (since they differ only by case and the slash immediately after the host).
var uri1 = new Uri("https://www.baidu.com/aaaa/bbbb");
var uri2 = new Uri("https://www.baidu.com/aaaa/bbbb/");
OK, this seems like the first scenario, but it isn't. The first scenario treats them as equivalent since it is 'pure domain name' (i.e. straight after the host). This is different (i.e. the slash is at the end, not straight after the host), and thus they aren't equivalent (on all web servers). Thus not equal.
var uri1 = new Uri("https://www.baidu.com/AAAaa/bbbb");
var uri2 = new Uri("https://www.baidu.com/aAAa/bbbb");
The path and querystring are case sensitive. Thus these are not equal. Some web servers / programming environments (e.g. ASP.NET MVC) may act case-insensitive, but according to the spec the path and querystring are case sensitive (since some web servers are case sensitive).
var uri1 = new Uri("https://www.baiDu.com/aaaa/bbbb");
var uri2 = new Uri("https://www.Baidu.com/aaaa/bbbb");
The only difference is the case of the host. Thus they are equal.
It's error! the error is: AmbiguousActionException: Multiple actions
matched. The following actions matched route data and had all
constraints satisfied:
This is because ASP.NET MVC is generally not case sensitive. Force case-sensitive routing in ASP.NET MVC may be useful for this part of your problem.

Error "405 Method Not Allow" When Calling Put method in Postman with body parameter

I was trying to call the Put method through Postman and always getting error: "405 Method Not Allow" and "Message": "The requested resource does not support http method 'PUT'."
I'm using DocumentDB and C#. Here is my code:
[Route("multilanguage/Resources/{id}/{Language}")]
[HttpPut]
public async Task<IHttpActionResult> UpdateResource(string Id, string Language, string text)
{
client = new DocumentClient(new Uri(EndPoint), AuthKey);
var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = #pmId",
new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "#pmId", Value = Id } }));
Document doc = client.CreateDocumentQuery<Document>(
collectionLink, query).AsEnumerable().FirstOrDefault();
List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
Models.Translations temp = d.Find(p => p.Language == Language);
temp.Content = text;
temp.LastModified = DateTimeOffset.Now;
temp.ModifiedBy = "admin";
doc.SetPropertyValue("Translations", d);
Document updated = await client.ReplaceDocumentAsync(doc);
return Ok();
}
When I call the Put method throught Postman, I call "http://localhost:XXXX/multilanguage/resources/2/En". "2" and "En" are the first two parameters in my code. And I also specify the "text" parameter value in the Postman request Body with x-www-form-urlencoded type: key = text, value = Test! This put method suppose to update the temp.Content value to "Test!". However, it always failed with the error I mentioned above.
Did I miss anything here?
The 405 error when performing a PUT request to web api is a well known topic. You can find many solutions in this or this SO question.
And for the design of you controller:
PUT are designed to have a body, just like POST and in your case
you should send all parameters in the body instead.
You should create a class which contains the objects you want to send to the server:
public class resourceClass
{
public string Id { get; set; }
public string Language { get; set; }
public string text { get; set; }
}
Then specify the route without the attribute routing and get the object from the request body
[Route("multilanguage/Resources/PutResource")]
[HttpPut]
public async Task<IHttpActionResult> UpdateResource([FromBody] resourceClass obj)
{
client = new DocumentClient(new Uri(EndPoint), AuthKey);
var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = #pmId",
new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "#pmId", Value = Id } }));
Document doc = client.CreateDocumentQuery<Document>(
collectionLink, query).AsEnumerable().FirstOrDefault();
List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
Models.Translations temp = d.Find(p => p.Language == Language);
temp.Content = text;
temp.LastModified = DateTimeOffset.Now;
temp.ModifiedBy = "admin";
doc.SetPropertyValue("Translations", d);
Document updated = await client.ReplaceDocumentAsync(doc);
return Ok();
}
From the client you could add an object to the PUT request of Content-Type application/json like this
var data = {
Id: clientId,
Language: clientLanguage,
text: clientText
};
Don't forget to stringify the json when adding it to the http request
data: JSON.stringify(data),
The PUT controller will then be reached at "http://localhost:XXXX/multilanguage/resources/putresource".
Check the URL for which you are posting the data, in my case the URL was incorrect because of which I got these errors, also verify that in Body you should select raw and change the Text to JSON if you are passing a JSON as a data.

How to build a Url?

Are there any helper classes available in .NET to allow me to build a Url?
For example, if a user enters a string:
stackoverflow.com
and i try to pass that to an HttpWebRequest:
WebRequest.CreateHttp(url);
It will fail, because it is not a valid url (it has no prefix).
What i want is to be able to parse the partial url the user entered:
Uri uri = new Uri(url);
and then fix the missing pieces:
if (uri.Port == 0)
uri.Port = 3333;
if (uri.Scheme == "")
uri.Scheme = "https";
Does .NET have any classes that can be used to parse and manipulate Uri's?
The UriBuilder class can't do the job
The value that the user entered (e.g. stackoverflow.com:3333) is valid; i just need a class to pick it apart. i tried using the UriBuilder class:
UriBuilder uriBuilder = new UriBuilder("stackoverflow.com:3333");
unfortunately, the UriBuilder class is unable to handle URIs:
uriBuilder.Path = 3333
uriBuilder.Port = -1
uriBuidler.Scheme = stackoverflow.com
So i need a class that can understand host:port, which especially becomes important when it's not particularly http, but could be.
Bonus Chatter
Console application.
From the other question
Some examples of URL's that require parsing:
server:8088
server:8088/func1
server:8088/func1/SubFunc1
http://server
http://server/func1
http://server/func/SubFunc1
http://server:8088
http://server:8088/func1
http://server:8088/func1/SubFunc1
magnet://server
magnet://server/func1
magnet://server/func/SubFunc1
magnet://server:8088
magnet://server:8088/func1
magnet://server:8088/func1/SubFunc1
http://[2001:db8::1]
http://[2001:db8::1]:80
The format of a Url is:
foo://example.com:8042/over/there?name=ferret#nose
\_/ \_________/ \__/\_________/\__________/ \__/
| | | | | |
scheme host port path query fragment
Bonus Chatter
Just to point out again that UriBuilder does not work:
https://dotnetfiddle.net/s66kdZ
If you need to ensure that some string coming as user input is valid url you could use the Uri.TryCreate method:
Uri uri;
string someUrl = ...
if (!Uri.TryCreate(someUrl, UriKind.Absolute, out uri))
{
// the someUrl string did not contain a valid url
// inform your users about that
}
else
{
var request = WebRequest.Create(uri);
// ... safely proceed with executing the request
}
Now if on the other hand you want to be building urls in .NET there's the UriBuilder class specifically designed for that purpose. Let's take an example. Suppose you wanted to build the following url: http://example.com/path?foo=bar&baz=bazinga#some_fragment where the bar and bazinga values are coming from the user:
string foo = ... coming from user input
string baz = ... coming from user input
var uriBuilder = new UriBuilder("http://example.com/path");
var parameters = HttpUtility.ParseQueryString(string.Empty);
parameters["foo"] = foo;
parameters["baz"] = baz;
uriBuilder.Query = parameters.ToString();
uriBuilder.Fragment = "some_fragment";
Uri finalUrl = uriBuilder.Uri;
var request = WebRequest.Create(finalUrl);
... safely proceed with executing the request
You can use the UriBuilder class.
var builder = new UriBuilder(url);
builder.Port = 3333
builder.Scheme = "https";
var result = builder.Uri;
To be valid a URI needs to have the scheme component. "server:8088" is not a valid URI. "http://server:8088" is. See https://www.rfc-editor.org/rfc/rfc3986

Absolute URL from base + relative URL in C#

I have a base URL :
http://my.server.com/folder/directory/sample
And a relative one :
../../other/path
How to get the absolute URL from this ? It's pretty straighforward using string manipulation, but I would like to do this in a secure way, using the Uri class or something similar.
It's for a standard a C# app, not an ASP.NET one.
var baseUri = new Uri("http://my.server.com/folder/directory/sample");
var absoluteUri = new Uri(baseUri,"../../other/path");
OR
Uri uri;
if ( Uri.TryCreate("http://base/","../relative", out uri) ) doSomething(uri);
Some might be looking for Javascript solution that would allow conversion of urls 'on the fly' when debugging
var absoluteUrl = function(href) {
var link = document.createElement("a");
link.href = href;
return link.href;
}
use like:
absoluteUrl("http://google.com")
http://google.com/
or
absoluteUrl("../../absolute")
http://stackoverflow.com/absolute

Categories