I've made a little helper function to deal with my HTTP requests that simply takes a string URL and a string array of headers formatted as Header-Name: Value
public static HttpWebResponse MakeHttpRequest(string URL, string[] Headers)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
req.Headers.Clear();
foreach (string h in Headers)
{
var s = h.Split(new char[] { ':' }, 2);
req.Headers.Set(s[0], s[1]);
}
return (HttpWebResponse)req.GetResponse();
}
But if the Headers array contains a Restricted header (e.g User-Agent, or Content-Type, or Accept, etc..) I get an exception telling me to modify the header using its property or method, so I was thinking maybe there was some way to check if any of the Headers was restricted and automatically modify it using its property, unfortunately, I'm not too smart so I don't really know how to do that without having a lot of bloated code checking every single restricted header and having different code run for each one
Im guessing it can be done with Reflection but I'm not sure... Help?
There is static method to check if header is restricted:
bool restricted = System.Net.WebHeaderCollection.IsRestricted(key); // key = s[0]
You can use reflection to set related property like this:
public static HttpWebResponse MakeHttpRequest(string URL, string[] Headers) {
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(URL);
req.Headers.Clear();
foreach (string h in Headers) {
var s = h.Split(new char[] {':'}, 2);
var key = s[0];
var value = s[1];
if (WebHeaderCollection.IsRestricted(key)) {
// remove "-" because some header names contain it, but .NET properties do not
key = key.Replace("-", "");
// get property with header name
var prop = typeof(HttpWebRequest).GetProperty(key, BindingFlags.Instance | BindingFlags.Public);
// set, changing type if necessary (some properties are long, or date, and your values are always strings)
prop.SetValue(req, Convert.ChangeType(value, prop.PropertyType, CultureInfo.InvariantCulture));
}
else {
req.Headers.Set(s[0], s[1]);
}
}
return (HttpWebResponse) req.GetResponse();
}
Ensure to test this code against all restricted headers (listed here) if you are going to use it, because I did not. Or just handle each header separately, because there are just 11 of them, not a hundred.
Related
I want to call a webpage by its IP address by adding custom values to request header for "host".
"" Code:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://1.1.1.1");
request.Headers["Host"] = "xyz.net";
WebResponse response = request.GetResponse();
But it gives an error:
ArgumentException: restricted header
It seems that some headers cannot be modified in .net 2.0
so is there any way that I can change the host or change the .net version in unity to higher version?
You can do this with reflection. Unfortunately, none of the C# answers from similar questions works because Unity is using Mono and their variable names are totally different making GetField unable to find the variable that's holding the headers.
Dump all the headers in the HttpWebRequest class with HttpWebRequest.GetType().GetFields then look for the name of the field that holds the headers. In my test, the field name is "webHeaders" and is a type of WebHeaderCollection.
Below is an extension method that modifies that "webHeaders" from reflection:
public static class ExtensionMethods
{
public static void changeSysTemHeader(this HttpWebRequest request, string key, string value)
{
WebHeaderCollection wHeader = new WebHeaderCollection();
wHeader[key] = value;
FieldInfo fildInfo = request.GetType().GetField("webHeaders",
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.GetField);
fildInfo.SetValue(request, wHeader);
}
public static void changeReflectionField(this HttpWebRequest request, string fieldName, object value)
{
FieldInfo fildInfo = request.GetType().GetField(fieldName, System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.GetField);
fildInfo.SetValue(request, value);
}
}
USAGE:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://1.1.1.1");
//Change Host header
request.changeSysTemHeader("Host", "xyz.net");
request.changeReflectionField("hostChanged", true);
WebResponse response = request.GetResponse();
This should work for any restricted header like User-Agent. Tested with Unity 2017.2. Mentioned the Unity version and how I found the field name so that when the variable name changes in the future, anyone can simply fix it.
I'm trying to update a resource using RestSharp. The API works well because are used in another application so this exclude some routing issues or whatever the problem is on my side not on API one.
Anyways. My current scenario is that I want to update a specific resource located at host/api/resource/id
This is my current code inside the DataProvider layer
public override bool Update(string resource, Dictionary<string, object> properties)
{
this.request = new RestRequest(resource + "/{id}", Method.PUT);
for (int i = 0; i < properties.Count; ++i)
{
KeyValuePair<string, object> kvp = properties.ElementAt(i);
if (kvp.Key != "id")
this.request.AddParameter(kvp.Key, kvp.Value, ParameterType.GetOrPost);
else
this.request.AddParameter(kvp.Key, kvp.Value, ParameterType.UrlSegment);
}
var response = this.CallApi();
// ... other stuff
}
This code simply create the request and the correct parameters based on the dictionary that the method received from outside, then it calls the CallApi() method which is this
private IRestResponse CallApi()
{
var client = new RestClient(BaseUrl);
var response = client.Execute(this.request);
if(response.ErrorException != null)
{
// Response has some error!
// ... other stuff
}
if(response.StatusCode != System.Net.HttpStatusCode.OK)
{
// Response received something different from HTTP status code OK
// ... other stuff
}
return response;
}
CallApi works perfectly for every other call such as GET, POST, DELETE and even PATCH but when I try to use it with Update, and thus using PUT, the response received from client.Execute(this.request) is 405 Method Not Allowed.
After debugging a little bit I figured it out that the respone has a ResponseUri with only the host string instead of host/api/resource/id this seems to be caused by the
this.request = new RestRequest(resource + "/{id}", Method.PUT);
in fact if I remove the /{id} part, the RequestUri has the correct form of host/api/resource, of course without the id, which is wrong anyway because I need the id :-/
Does anyone know why this is happening?
The problem was at the backslash on new Instance. Just remove the backslash from /{id} and it works
I'm newbie to using the dynamic keyword in C#. It seems simple enough, but I can't seem to use it effectively.
I see this example from Facebook:
var client = new FacebookClient();
dynamic me = client.Get("totten");
string firstname = me.first_name;
it works fine, but if you look at me in a debugger, then you can see that client.Get() returns simple JSON. The same it's said in Facebook documentation:
The result of this request is a dynamic object containing various
properties such as first_name, last_name, user name, etc. You can see
the values of this request by browsing to
http://graph.facebook.com/totten in your web browser. The JSON result
is shown below.
I want to do the same dodge with returned JSON from Foursquare:
private static string GetReturnedUrlFromHttp(string url)
{
HttpWebRequest webRequest = WebRequest.Create(url) as HttpWebRequest;
webRequest.Timeout = 10000;
webRequest.Method = "GET";
WebResponse response = webRequest.GetResponse();
string responseStr = String.Empty;
using (var stream = response.GetResponseStream())
{
var r = new StreamReader(stream);
responseStr = r.ReadToEnd();
}
return responseStr;
}
public static void FillDataFromFoursquareUsingDynamic()
{
string foursquare_url_detail = "https://api.foursquare.com/v2/venues/4b80718df964a520e57230e3?locale=en&client_id=XXX&client_secret=YYY&v=10102013";
dynamic responseStr = GetReturnedUrlFromHttp(foursquare_url_detail);
var response = responseStr.response;
}
I got the following error:
'string' does not contain a definition for 'response'
Why am I getting this error and is it possible to 'parse' any JSON string like in Facebook?
FacebookClient.Get doesn't really return the JSON string. Instead it parses the string into a dynamic object with properties matching the names of the values in the JSON string.
Using dynamic doesn't magically turn a string into an object with the properties defined in the string. Instead, you need to first parse the string with the help of a JSON library like JSON.NET.
I have the following function which makes a HTTP request and returns the response body as a string:
private string getResponseBody(string method, string partialUrl)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(_hostPath + partialUrl);
req.ContentLength = 0;
req.KeepAlive = false;
req.Method = method;
return new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd();
}
The first parameter, method, can only have the general HTTP methods as values (GET, POST, PUT, DELETE).
To force the input to be one of these values, I know I would need to create an object, but the details are escaping me. Can anyone help out?
PS: I'm using 4.0 framework
To force the input to be one of these values, I know I would need to create an object,
How about an enum:
public enum HttpMethod
{
GET,
POST,
PUT,
DELETE
}
and then:
private string getResponseBody(HttpMethod method, string partialUrl)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(_hostPath + partialUrl);
req.ContentLength = 0;
req.KeepAlive = false;
req.Method = method.ToString();
using (var response = req.GetResponse())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
Also notice how I have fixed the flaw you had in the GetResponse code where you didn't dispose any of the IDisposable objects which could result in leaks and all kind of nasty stuff. For example if you don't dispose the response object it could result in timeouts if this method is called many times.
This occurred within the context of coding against Google Cloud Messaging, but applies elsewhere.
Consider the following:
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("key=XXX");
and
var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "key=XXX");
both of which generate a FormatException:
System.FormatException : The format of value key=XXX' is invalid.
The solution is to remove the equals sign.
Digging into reflector shows there is oodles of validation and parsing code that runs when adding a a new header value. Why is all this necessary? Shouldn't this client just be getting out of our way?
How do you escape the equals sign so that adding this value succeeds?
Not sure if still relevant, but I recently ran into this same issue and was able to solve it by calling a different method to add the header information:
var http = new HttpClient();
http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=XXX");
To your "why is all this (parsing and validation) necessary" question, the answer is: it is defined in the HTTP standard.
In HTTP/1.1 and RFC2617, the value an authentication header (such as WWW-Authenticate and Authorization) has two parts: a scheme part, and a parameter part.
For HTTP Basic Authentication, the scheme is "Basic", and the parameter may be something like "QWxhZGRpbjpvcGVuIHNlc2FtZQ==", so the whole header becomes:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
That's why your "key=XXX" doesn't pass validation, because it lacks a scheme part.
I ran into this error and stumbled on to this post when I added a space to the end of an Authorization header.
this.bearerAuthHttpClient.DefaultRequestHeaders.Add("Authorization ", $"Bearer {token}");
You can see the offending " " after Authorization.
It took me about 15 min before I saw my typo...
I got around this exception (my FormatException caused by commas in the value) by setting the Authorization header in the following way:
var authenticationHeaderValue = new AuthenticationHeaderValue("some scheme", "some value");
client.DefaultRequestHeaders.Authorization = authenticationHeaderValue;
I've been going through a few questions this morning while dealing with an external API that doesn't follow the HTTP spec to the letter.
As part of my posting, they want the Content-Type and Content-Disposition, which cannot be added to the HttpClient object. To add those headers, you need to create an HttpRequestMessage. On there, you need to add the headers to the Content property.
private HttpRequestMessage GetPostMessage(string uri, string contentType,
string fileName, Stream content)
{
var request = new HttpRequestMessage
{
Content = new StreamContent(content),
RequestUri = new Uri(uri),
Method = HttpMethod.Post
};
// contentType = "video/mp4"
request.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
//Need TryAddWithoutValidation because of the equals sign in the value.
request.Content
.Headers
.TryAddWithoutValidation("Content-Disposition",
$"attachment; filename=\"{Path.GetFileName(fileName)}\"");
// If there is no equals sign in your content disposition, this will work:
// request.Content.Headers.ContentDisposition =
// new ContentDispositionHeaderValue($"attachment; \"{Path.GetFileName(fileName)}\"");
return request;
}
In my case I am generating ETags string values from a byte[] RowVersion SQL field.
So I need to add wrap the generated. i.e. AAAAAAAAF5s= string inside " as follows...
var eTag = department.RowVersion.ToETagString();
httpClient.DefaultRequestHeaders.Add(Microsoft.Net.Http.Headers.HeaderNames.IfMatch, $"\"{eTag}\"")
public class DepartmentForHandleDto
{
public string Name { get; set; }
public string GroupName { get; set; }
public byte[] RowVersion { get; set; }
}
public static class ByteArrayExtensions
{
public static string ToETagString(this byte[] byteArray)
{
return Convert.ToBase64String(byteArray != null && byteArray.Length > 0 ? byteArray : new byte[8]);
}
}