I was able to get this to run without any error being thrown however after it gets to the line "string responseData = new StreamReader(response.GetResponseStream()).ReadToEnd();" it just hangs. I have a breakpoint on the next line and still nothing. Am I not using this correctly? This is in a windows forms application. What do I need to do in order to parse this out? How do I grab the data while keeping the stream open?
My goal with this is to be able to grab tweets and sort them based on criteria TBD.
var oAuthToken = "****";
var oAuthTokenSecret = "****";
var oAuthConsumerKey = "****";
var oAuthConsumerSecret = "****";
var oAuthVersion = "1.0";
var oAuthSignatureMethod = "HMAC-SHA1";
var oAuthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var oAuthTimeStamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
var resourceURL = "https://stream.twitter.com/1.1/statuses/filter.json";
var locations = "-180,-90,180,90";
var baseFormat = "locations={6}&oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
"&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}";
var baseString = string.Format(baseFormat,
oAuthConsumerKey,
oAuthNonce,
oAuthSignatureMethod,
oAuthTimeStamp,
oAuthToken,
oAuthVersion,
Uri.EscapeDataString(locations)
);
baseString = string.Concat("POST&", Uri.EscapeDataString(resourceURL), "&", Uri.EscapeDataString(baseString));
var compositeKey = string.Concat(Uri.EscapeDataString(oAuthConsumerSecret),
"&", Uri.EscapeDataString(oAuthTokenSecret));
string oAuthSignature;
using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey)))
{
oAuthSignature = Convert.ToBase64String(
hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString)));
}
// create the request header
var headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " +
"oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " +
"oauth_token=\"{4}\", oauth_signature=\"{5}\", " +
"oauth_version=\"{6}\"";
var authHeader = string.Format(headerFormat,
Uri.EscapeDataString(oAuthNonce),
Uri.EscapeDataString(oAuthSignatureMethod),
Uri.EscapeDataString(oAuthTimeStamp),
Uri.EscapeDataString(oAuthConsumerKey),
Uri.EscapeDataString(oAuthToken),
Uri.EscapeDataString(oAuthSignature),
Uri.EscapeDataString(oAuthVersion)
);
//make the request
ServicePointManager.Expect100Continue = false;
var postBody = "locations=" + Uri.EscapeDataString(locations);
resourceURL += "?" + postBody;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(resourceURL);
request.Headers.Add("Authorization", authHeader);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
try
{
WebResponse response = request.GetResponse();
string responseData = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
catch (Exception exception)
{
throw (exception);
}
Well it's a streaming API, so it will stream infinitely. Hence if you are trying to read to the end of stream, it will block forever until you run out of memory or get disconnected due to network error etc.
You need to process the data while connected to the stream. If I were you I'd just use a client library (see https://dev.twitter.com/docs/twitter-libraries).
Related
I've created a public Data Entity in dynamics with the following fields:
I keep getting a bad request response, but I'm not sure why.
I've tried to make a POST request in two ways:
1.
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionStatus = "Created" };
string jsonMessage = JsonConvert.SerializeObject(hireAction);
using (HttpClient client = new HttpClient())
{
HttpRequestMessage requestMessage = new
HttpRequestMessage(HttpMethod.Post, "MyDynamicsEnvironmentName/data/HireActions?cross-company=true");
requestMessage.Content = new StringContent(jsonMessage, Encoding.UTF8, "application/json");
requestMessage.Headers.Add("Authorization", AuthResult.AuthorizationHeader);
HttpResponseMessage response = client.SendAsync(requestMessage).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
//Logic
}
}
var url = "MyDynamicsEnvironmentName/data/HireActions?cross-company=true";
var req = HttpWebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/json";
req.Headers["Authorization"] = AuthResult.AuthorizationHeader;
HireAction hireAction = new HireAction() { CompanyName = "DEMF", MovieId = "DEMF-000000014", HireActionId = "12345", HireActionStatus = "Created" };
var jsonSettings = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Local
};
var postString = "CompanyName='DEMF'" + "&MovieId='DEMF-000000014'" + "&HireActionId=132&HireActionStatus='Created'";
var data = JsonConvert.SerializeObject(postString, jsonSettings);
var bytes = Encoding.Default.GetBytes(postString);
var newStream = req.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
using (var resp = req.GetResponse())
{
var results = new StreamReader(resp.GetResponseStream()).ReadToEnd();
}
Some keypoints:
-Of course you'd replace MyDynamicsEnvironmentName with the URL for the environment. The URL is correct and verified however, by the fact that GET requests do work
-The Authresult.AuthorizationHeader contains a valid token, also validated by working GET requests
As said before, both of these result in a bad request. Does someone know what is wrong or missing?
I have a scenario where I need to use an OnPrem-SharePoint for File Upload/Download. It's SharePoint 2016 and I found a helper class from the old days that uses WebRequests to communicate. I must use the same class in my .net 7 Minimal API project. It works but I don't like the tech debt I have to carry by using WebRequests. There are quite a few methods there but I need help converting these main ones and I can manage the rest as they are somewhat copy-paste:
public partial class SharePointService : ISharePointService
{
private const string AuthUrl = "https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2";
private readonly SPAppInit spAppInit;
public SharePointService(SPAppInit spAppInit) => this.spAppInit = spAppInit;
public SPAuthToken? GetAuthToken()
{
var postParameters = new[]
{
new KeyValuePair<string, string>("grant_type", spAppInit.GrantType),
new KeyValuePair<string, string>("client_id", $"{spAppInit.AppClientId}#{spAppInit.TenantId}"),
new KeyValuePair<string, string>("client_secret", spAppInit.AppSecret),
new KeyValuePair<string, string>("resource", $"00000000-0000-0aaa-ce00-000000000000/{spAppInit.TenantName}#{spAppInit.TenantId}"),
};
var postData = postParameters.Aggregate("", (current, t) => current + HttpUtility.UrlEncode(t.Key) + "=" + HttpUtility.UrlEncode(t.Value) + "&");
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(string.Format(AuthUrl, spAppInit.TenantId));
myHttpWebRequest.Method = "POST";
var data = Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
var requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
var myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
var responseStream = myHttpWebResponse.GetResponseStream();
var myStreamReader = new StreamReader(responseStream, Encoding.Default);
var result = myStreamReader.ReadToEnd();
var authToken = JsonSerializer.Deserialize<SPAuthToken>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return authToken;
}
public string GetRequestDigest(string authToken)
{
const string endpoint = "/_api/contextInfo";
var request = (HttpWebRequest)WebRequest.Create(spAppInit.SPSiteUrl + endpoint);
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
//request.Accept = "application/json;odata=verbose";
request.ContentLength = 0;
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
// parse the ContextInfo response
var resultXml = XDocument.Parse(result);
// get the form digest value
var e = from r in resultXml.Descendants()
where r.Name == XName.Get("FormDigestValue", "http://schemas.microsoft.com/ado/2007/08/dataservices")
select r;
return e.First().Value;
}
public string CreateFolder(string authToken, string requestDigest, string folderName)
{
var request = (HttpWebRequest)WebRequest.Create($"{spAppInit.SPSiteUrl}/_api/web/getfolderbyserverrelativeurl('{spAppInit.SPDocLibServerRelativeUrl}/')/folders");
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.Headers["Authorization"] = $"Bearer {authToken}";
request.Headers["X-RequestDigest"] = requestDigest;
request.Accept = "application/json;";
request.ContentType = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
var requestParams = CreateRequest(folderName);
var json = JsonSerializer.Serialize(requestParams);
streamWriter.Write(json);
}
using var response = (HttpWebResponse)request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var result = reader.ReadToEnd();
var folderCreationResponse = JsonSerializer.Deserialize<SPFolderCreationResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return folderCreationResponse?.ServerRelativeUrl ?? string.Empty;
}
public void UploadLargeFile(string authToken, string formDigest, string sourcePath, string targetFolderUrl, int chunkSizeBytes = 2048)
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var fileName = Path.GetFileName(sourcePath);
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = File.OpenRead(sourcePath);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/getFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
public void UploadLargeFileUsingBytes(string authToken, string formDigest, string fileName, byte[] fileBytes, string targetFolderUrl, int chunkSizeBytes = 2048)
{
if (fileBytes.Length < chunkSizeBytes)
{
UploadSmallFile(authToken, formDigest, targetFolderUrl, fileName, fileBytes);
}
else
{
using var client = new WebClient();
client.BaseAddress = spAppInit.SPSiteUrl;
client.Headers.Add("X-RequestDigest", formDigest);
client.Headers.Add("content-type", "application/json;odata=verbose");
client.Headers.Add("Authorization", $"Bearer {authToken}");
var createFileRequestUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFolderByServerRelativeUrl('{targetFolderUrl}')/files/add(url='{fileName}',overwrite=true)";
client.UploadString(createFileRequestUrl, "POST");
var targetUrl = Path.Combine(targetFolderUrl, fileName);
var firstChunk = true;
var uploadId = Guid.NewGuid();
var offset = 0L;
using var inputStream = ReadFileStreamFromByte(fileBytes);
var buffer = new byte[chunkSizeBytes];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (firstChunk && inputStream.Length > chunkSizeBytes)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/startUpload(uploadId=guid'{uploadId}')";
client.UploadData(endpointUrl, buffer);
firstChunk = false;
}
else if (inputStream.Position == inputStream.Length)
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/finishUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
var finalBuffer = new byte[bytesRead];
Array.Copy(buffer, finalBuffer, finalBuffer.Length);
client.UploadData(endpointUrl, finalBuffer);
}
else
{
var endpointUrl = $"{spAppInit.SPSiteUrl}/_api/web/GetFileByServerRelativeUrl('{targetUrl}')/continueUpload(uploadId=guid'{uploadId}',fileOffset={offset})";
client.UploadData(endpointUrl, buffer);
}
offset += bytesRead;
Console.WriteLine("%{0:P} completed", offset / (float)inputStream.Length);
}
}
}
}
I don't like pasting the whole project. However, even after being very selective, this is still a lot of code, but its communication code that I do not understand as I started with the newer HttpClient only as (some random HttpClient injection in my API):
services.AddCustomHttpClient<ILetterServiceApi, LetterServiceApi>(Constants.ApiServiceNamedClientLetter, baseAddress!);
Where AddCustomHttpClient is,
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomHttpClient<TInterface, TImplementation>(this IServiceCollection services, string serviceName, string baseAddress)
where TInterface: class
where TImplementation: class, TInterface
{
services.AddHttpClient<TInterface, TImplementation>(serviceName, config =>
{
config.BaseAddress = new Uri(baseAddress);
config.Timeout = new TimeSpan(0, 0, 30);
})
.AddHttpMessageHandler<RequestHandler>();
return services;
}
}
I have made a lot of modern modifications to the code and also Am in the process of converting this class (in a remote assembly) to conform to the .Net Core's .AddSharePoint() DI pattern, and for that, I need to get rid of obsolete (HttpWebRequest)WebRequest
Update:
I managed to convert a rather simple method:
private SPFolderResponse? GetFolderFiles(string authToken, string requestDigest, string folderName)
{
using var client = new HttpClient();
var serverRelativeFolderUrl = $"{spAppInit.RelativeUrl}/{folderName}";
var folderApiUrl = $"{spAppInit.SiteUrl}/_api/web/GetFolderByServerRelativeUrl('/{serverRelativeFolderUrl}')/Files";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-RequestDigest", requestDigest);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {authToken}");
var result = client.GetStringAsync(folderApiUrl).GetAwaiter().GetResult();
var response = JsonSerializer.Deserialize<SPFolderResponse>(result, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return response;
}
But the other ones like the Token, Digest and Upload ones are still dodgy...
public HttpWebResponse PushFileToWistia(byte[] contentFileByteArray, string fileName)
{
StringBuilder postDataBuilder = new StringBuilder();
postDataBuilder.Append("I am appending all the wistia config and setting here");
byte[] postData = null;
using (MemoryStream postDataStream = new MemoryStream())
{
byte[] postDataBuffer = Encoding.UTF8.GetBytes(postDataBuilder.ToString());
postDataStream.Write(postDataBuffer, 0, postDataBuffer.Length);
postDataStream.Write(contentFileByteArray, 0, contentFileByteArray.Length);
postDataBuffer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--");
postDataStream.Write(postDataBuffer, 0, postDataBuffer.Length);
postData = postDataStream.ToArray();
}
ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(AppConfig.WistiaCustomCourseBucket);
request.Method = "POST";
request.Expect = String.Empty;
request.Headers.Clear();
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.ContentLength = postData.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(postData, 0, postData.Length); //for file > 100mb this call throws and error --the requet was aborted. the request was canceled.
requestStream.Flush();
requestStream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return response;
}
The above code works for video file mp4 less then 50mb. But when I try to upload a 100mb file it throws and exception (Request was aborted.) I need to support file size up to 1.5gb So now I am not sure if this approach is correct for such a big file size upload. Any suggestions in the right direction will be helpful...thanks(I am trying to upload the file to Wistia Server)
The exception is thrown at this line
-- requestStream.Write(postData, 0, postData.Length);
I have tried changing the web.config setting but didn't work:
httpRuntime targetFramework="4.5" maxRequestLength="2048576" executionTimeout="12000" requestLengthDiskThreshold="1024"
------Async Call-------
MemoryStream wistiaFileStream = null;
using (MemoryStream postDataStream = new MemoryStream())
{
postDataStream.Write(contentFileByteArray, 0, contentFileByteArray.Length);
wistiaFileStream = postDataStream;
postDataStream.Flush();
postDataStream.Close();
}
Stream requestStream = await request.GetRequestStreamAsync();
await requestStream.WriteAsync(wistiaMetadata, 0, wistiaMetadata.Length);
using (wistiaFileStream)
{
byte[] wistiaFileBuffer = new byte[500*1024];
int wistiaFileBytesRead = 0;
while (
(wistiaFileBytesRead =
await wistiaFileStream.ReadAsync(wistiaFileBuffer, 0, wistiaFileBuffer.Length)) != 0)
{
await requestStream.WriteAsync(wistiaFileBuffer, 0, wistiaFileBytesRead);
await requestStream.FlushAsync();
}
await requestStream.WriteAsync(requestBoundary, 0, requestBoundary.Length);
}
I would suggest moving to async and write file directly from file system to request in order to avoid triple buffering of 1.5GB in memory (warning below is not tested).
public async Task<HttpWebResponse> PushFileToWistiaAsync(string contentFilePath)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
string contentBoundary = "\r\n--" + boundary + "\r\n";
StringBuilder wistiaMetadataBuilder = new StringBuilder();
wistiaMetadataBuilder.Append("--" + boundary + "\r\n");
// Append all the wistia config and setting here
byte[] wistiaMetadata = Encoding.UTF8.GetBytes(wistiaMetadataBuilder.ToString());
byte[] requestBoundary = Encoding.UTF8.GetBytes(contentBoundary);
ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(AppConfig.WistiaCustomCourseBucket);
request.Method = "POST";
request.Headers.Clear();
request.Expect = String.Empty;
request.ContentType = "multipart/form-data; boundary=" + boundary;
Stream requestStream = await request.GetRequestStreamAsync();
await requestStream.WriteAsync(wistiaMetadata, 0, wistiaMetadata.Length);
using (FileStream wistiaFileStream = new FileStream(contentFilePath, FileMode.Open, FileAccess.Read))
{
byte[] wistiaFileBuffer = new byte[500 * 1024];
int wistiaFileBytesRead = 0;
while ((wistiaFileBytesRead = await wistiaFileStream.ReadAsync(wistiaFileBuffer, 0, wistiaFileBuffer.Length)) != 0)
{
await requestStream.WriteAsync(wistiaFileBuffer, 0, wistiaFileBytesRead);
await requestStream.FlushAsync();
}
}
await requestStream.WriteAsync(requestBoundary, 0, requestBoundary.Length);
return (HttpWebResponse)(await request.GetResponseAsync());
}
You should play with buffer sizes, amount of data you read at once and request.SendChunked to achieve reasonable performance.
Here is another approach (not asynchronous so possibly worst scalability) which wirtes directly from buffer to request:
public HttpWebResponse PushFileToWistia(byte[] contentFileByteArray)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
string contentBoundary = "\r\n--" + boundary + "\r\n";
StringBuilder wistiaMetadataBuilder = new StringBuilder();
wistiaMetadataBuilder.Append("--" + boundary + "\r\n");
// Append all the wistia config and setting here
byte[] wistiaMetadata = Encoding.UTF8.GetBytes(wistiaMetadataBuilder.ToString());
byte[] requestBoundary = Encoding.UTF8.GetBytes(contentBoundary);
ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(AppConfig.WistiaCustomCourseBucket);
request.Method = "POST";
request.Headers.Clear();
request.Expect = String.Empty;
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.ContentLength = wistiaMetadata.Length + contentFileByteArray.Length + requestBoundary.Length
// You can play with SendChunked and AllowWriteStreamBuffering to control the size of chunks you send and performance
//request.SendChunked = true;
//request.AllowWriteStreamBuffering = false;
int contentFileChunkSize = 500 * 1024;
int contentFileBytesRead = 0;
Stream requestStream = request.GetRequestStream();
requestStream.Write(wistiaMetadata, 0, wistiaMetadata.Length);
while (contentFileBytesRead < contentFileByteArray.Length)
{
if ((contentFileBytesRead + contentFileChunkSize) > contentFileByteArray.Length)
{
contentFileChunkSize = contentFileByteArray.Length - contentFileBytesRead;
}
requestStream.Write(contentFileByteArray, contentFileBytesRead, contentFileChunkSize);
requestStream.Flush();
contentFileBytesRead += contentFileChunkSize;
}
requestStream.Write(requestBoundary, 0, requestBoundary.Length);
requestStream.Close();
// You might need to play with request.Timeout here
return (HttpWebResponse)request.GetResponse();
}
Also if you doing this in web application and you want to use asynchronous approach you need to "async/await" all the way up (so async action in async controller etc.).
In general I would discourage doing this as part of request handling in web application (the total time observed from user perspective would be a sum of uploading to your app and then to Wistia which might be much more than client timeout allows). In such case it is usually better to save the file and schedule some other "background task" to do the upload job.
Below code helped me to implement REST API calls with Twitter
var oauth_token = membership.Token; //"insert here...";
var oauth_token_secret = membership.TokenSecret; //"insert here...";
var oauth_consumer_key = consumerKey;
var oauth_consumer_secret = consumerSecret;
// oauth implementation details
var oauth_version = "1.0";
var oauth_signature_method = "HMAC-SHA1";
// unique request details
var oauth_nonce = Convert.ToBase64String(
new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
var timeSpan = DateTime.UtcNow
- new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
// create oauth signature
var baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
"&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}";
var baseString = string.Format(baseFormat,
oauth_consumer_key,
oauth_nonce,
oauth_signature_method,
oauth_timestamp,
oauth_token,
oauth_version
);
baseString = string.Concat("GET&", Uri.EscapeDataString(resource_url), "&", Uri.EscapeDataString(baseString));
var compositeKey = string.Concat(Uri.EscapeDataString(oauth_consumer_secret),
"&", Uri.EscapeDataString(oauth_token_secret));
string oauth_signature;
using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey)))
{
oauth_signature = Convert.ToBase64String(
hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString)));
}
// create the request header
var headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " +
"oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " +
"oauth_token=\"{4}\", oauth_signature=\"{5}\", " +
"oauth_version=\"{6}\"";
var authHeader = string.Format(headerFormat,
Uri.EscapeDataString(oauth_nonce),
Uri.EscapeDataString(oauth_signature_method),
Uri.EscapeDataString(oauth_timestamp),
Uri.EscapeDataString(oauth_consumer_key),
Uri.EscapeDataString(oauth_token),
Uri.EscapeDataString(oauth_signature),
Uri.EscapeDataString(oauth_version)
);
ServicePointManager.Expect100Continue = false;
// make the request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(resource_url);
request.Headers.Add("Authorization", authHeader);
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";
var response = (HttpWebResponse)request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
var objText = reader.ReadToEnd();
return objText.ToString();
But the problem is that when I tried to implement https://api.twitter.com/1.1/statuses/lookup.json?id=20,432656548536401920 api it shows 401 error. The error comes only this api call only.
I am developing an app for iOS using Xamarin iOS & MonoGame. I want to use Parse's push notifications through their REST API, so first I must create an installation object:
var bundle = new Dictionary<string, object>();
bundle.Add("channels", "");
bundle.Add("deviceType", "ios");
bundle.Add("deviceToken", _deviceToken);
string urlpath = "https://api.parse.com/1/installations";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(urlpath);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("X-Parse-Application-Id", _parseAppID);
httpWebRequest.Headers.Add("X-Parse-REST-API-KEY", _parseRestAPIKey);
httpWebRequest.Method = "POST";
string bundleString = bundle.ToJson();
byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(bundleString);
string result = Convert.ToBase64String(buffer);
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(result, 0, result.Length);
requestWriter.Close();
WebResponse httpResponse = await httpWebRequest.GetResponseAsync();
Stream stream = httpResponse.GetResponseStream();
string json = string.Empty;
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
json += reader.ReadLine();
}
}
JsonObject jsonObject = JsonObject.Parse(json);
_varStorage.Save("ObjectId", jsonObject.Get<string>("objectId"));
The bundleString value is:
"{\"channels\":\"\",\"deviceType\":\"ios\",\"deviceToken\":\"46becd0a165be042eeab5a1ec96b8858065cbea7311479da16c0fd1c9428e2eb\"}"
This code raises a System.Net.WebExceptionStatus.ProtocolError error 400 "Bad Request", and I can't see why.
Channels is supposed to be an array of strings according to the documentation, https://www.parse.com/docs/rest#installations
bundle.Add("channels", new [] { "" });
After more trail and error, I found that replacing this
byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(bundleString);
string result = Convert.ToBase64String(buffer);
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(result, 0, result.Length);
requestWriter.Flush();
requestWriter.Close();
with this
httpWebRequest.ContentLength = bundleString.Length;
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(bundleString);
requestWriter.Flush();
requestWriter.Close();
fixed the problem, I don't know exactly why though.
should you not be calling flush before closing your stream ?
requestWriter.Write(result, 0, result.Length);
requestWriter.Close();