I created a WebAPI but now I want to secure it with Basic Authorization.
// POST the data to the API
using (var client = new WebClient())
{
client.Headers.Add("Content-Type", "application/json");
client.Headers.Add(HttpRequestHeader.Authorization, "Basic" + Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials)));
string json = JsonConvert.SerializeObject(ex);
string content = client.UploadString("http://myURL/v1/endpoint", json);
}
Below, how I post the data. Now, I would like to create a function that I can add to my controller or my Application_Start(). It will check:
if the request.Headers.Authorization is != null
if the request.Headers.Authorization.Scheme is != "Basic"
if there are some parameters
get the parameter and decode it to create a pair (SecretId/SecretKey)
call a service to check in the DB if there is a client with this pair
create an identity with IPrincipal
The thing is I don't know the best way is to create a customAttribute or a filter or something else. There is plenty of different way to do this but I would like to understand the difference.
Create the below-mentioned Filter in your project and use it at top of your web API method as :
**[BasicAuth]**
/// <summary>
/// Basic Authentication Filter Class
/// </summary>
public class BasicAuthAttribute : ActionFilterAttribute
{
/// <summary>
/// Called when [action executing].
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuting(HttpActionContext filterContext)
{
try
{
if (filterContext.Request.Headers.Authorization == null)
{
// Client authentication failed due to invalid request.
filterContext.Response = new System.Net.Http.HttpResponseMessage()
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent("{\"error\":\"invalid_client\"}", Encoding.UTF8, "application/json")
};
filterContext.Response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Basic", "realm=xxxx"));
}
else if (filterContext.Request.Headers.Authorization.Scheme != "Basic" ||
string.IsNullOrEmpty(filterContext.Request.Headers.Authorization.Parameter))
{
// Client authentication failed due to invalid request.
filterContext.Response = new System.Net.Http.HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest,
Content = new StringContent("{\"error\":\"invalid_request\"}", Encoding.UTF8, "application/json")
};
}
else
{
var authToken = filterContext.Request.Headers.Authorization.Parameter;
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(authToken));
int seperatorIndex = usernamePassword.IndexOf(':');
string clientId = usernamePassword.Substring(0, seperatorIndex);
string clientSecret = usernamePassword.Substring(seperatorIndex + 1);
if (!ValidateApiKey(clientId, clientSecret))
{
// Client authentication failed due to invalid credentials
filterContext.Response = new System.Net.Http.HttpResponseMessage()
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent("{\"error\":\"invalid_client\"}", Encoding.UTF8, "application/json")
};
}
// Successfully finished HTTP basic authentication
}
}
catch (Exception ex)
{
// Client authentication failed due to internal server error
filterContext.Response = new System.Net.Http.HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest,
Content = new StringContent("{\"error\":\"invalid_request\"}", Encoding.UTF8, "application/json")
};
}
}
/// <summary>
/// Validates the API key.
/// </summary>
/// <param name="recievedKey">The recieved key.</param>
/// <returns></returns>
private bool ValidateApiKey(string clientId, string clientSecret)
{
if (your condition satisfies)
{
return true;
}
return false;
}
}
I found few interesting articles about handlers/filter and attribute. I don't want to override [Authorize] so I will probably do an Authentication Filter.
Below some good links:
filter with attribute
filter
handler
#Nkosi: Cheers to confirm. I'm going to change the code a little bit because I don't want to use an Attribute but rather an filter that I put in the WebApiConfig
Related
I developed an ASP.NET Core C# Web API Console App project for learning purposes. I want to integrate the Google External Login/Authentication for my Web API. As an exercise, I want to authenticate a user with google from a console application using HttpClient, retrieve a token, and then use this token with every request to my Web API endpoints. I can't find any useful tutorial(s) that explain how to do this, both in the console app and setting up the Web API. How can I accomplish this? Thanks.
There is a great sample by google for .net console app in this repo, take a look at the code
https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthConsoleApp
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace OAuthConsoleApp
{
class Program
{
const string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
static async Task<int> Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Required command line arguments: client-id client-secret");
return 1;
}
string clientId = args[0];
string clientSecret = args[1];
Console.WriteLine("+-----------------------+");
Console.WriteLine("| Sign in with Google |");
Console.WriteLine("+-----------------------+");
Console.WriteLine("");
Console.WriteLine("Press any key to sign in...");
Console.ReadKey();
Program p = new Program();
await p.DoOAuthAsync(clientId, clientSecret);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
return 0;
}
// ref http://stackoverflow.com/a/3978040
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
private async Task DoOAuthAsync(string clientId, string clientSecret)
{
// Generates state and PKCE values.
string state = GenerateRandomDataBase64url(32);
string codeVerifier = GenerateRandomDataBase64url(32);
string codeChallenge = Base64UrlEncodeNoPadding(Sha256Ascii(codeVerifier));
const string codeChallengeMethod = "S256";
// Creates a redirect URI using an available port on the loopback address.
string redirectUri = $"http://{IPAddress.Loopback}:{GetRandomUnusedPort()}/";
Log("redirect URI: " + redirectUri);
// Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectUri);
Log("Listening..");
http.Start();
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
AuthorizationEndpoint,
Uri.EscapeDataString(redirectUri),
clientId,
state,
codeChallenge,
codeChallengeMethod);
// Opens request in the browser.
Process.Start(authorizationRequest);
// Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings the Console to Focus.
BringConsoleToFront();
// Sends an HTTP response to the browser.
var response = context.Response;
string responseString = "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
await responseOutput.WriteAsync(buffer, 0, buffer.Length);
responseOutput.Close();
http.Stop();
Log("HTTP server stopped.");
// Checks for errors.
string error = context.Request.QueryString.Get("error");
if (error is object)
{
Log($"OAuth authorization error: {error}.");
return;
}
if (context.Request.QueryString.Get("code") is null
|| context.Request.QueryString.Get("state") is null)
{
Log($"Malformed authorization response. {context.Request.QueryString}");
return;
}
// extracts the code
var code = context.Request.QueryString.Get("code");
var incomingState = context.Request.QueryString.Get("state");
// Compares the receieved state to the expected value, to ensure that
// this app made the request which resulted in authorization.
if (incomingState != state)
{
Log($"Received request with invalid state ({incomingState})");
return;
}
Log("Authorization code: " + code);
// Starts the code exchange at the Token Endpoint.
await ExchangeCodeForTokensAsync(code, codeVerifier, redirectUri, clientId, clientSecret);
}
async Task ExchangeCodeForTokensAsync(string code, string codeVerifier, string redirectUri, string clientId, string clientSecret)
{
Log("Exchanging code for tokens...");
// builds the request
string tokenRequestUri = "https://www.googleapis.com/oauth2/v4/token";
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
Uri.EscapeDataString(redirectUri),
clientId,
codeVerifier,
clientSecret
);
// sends the request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestUri);
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
byte[] tokenRequestBodyBytes = Encoding.ASCII.GetBytes(tokenRequestBody);
tokenRequest.ContentLength = tokenRequestBodyBytes.Length;
using (Stream requestStream = tokenRequest.GetRequestStream())
{
await requestStream.WriteAsync(tokenRequestBodyBytes, 0, tokenRequestBodyBytes.Length);
}
try
{
// gets the response
WebResponse tokenResponse = await tokenRequest.GetResponseAsync();
using (StreamReader reader = new StreamReader(tokenResponse.GetResponseStream()))
{
// reads response body
string responseText = await reader.ReadToEndAsync();
Console.WriteLine(responseText);
// converts to dictionary
Dictionary<string, string> tokenEndpointDecoded = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
string accessToken = tokenEndpointDecoded["access_token"];
await RequestUserInfoAsync(accessToken);
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
Log("HTTP: " + response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
// reads response body
string responseText = await reader.ReadToEndAsync();
Log(responseText);
}
}
}
}
}
private async Task RequestUserInfoAsync(string accessToken)
{
Log("Making API Call to Userinfo...");
// builds the request
string userinfoRequestUri = "https://www.googleapis.com/oauth2/v3/userinfo";
// sends the request
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestUri);
userinfoRequest.Method = "GET";
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", accessToken));
userinfoRequest.ContentType = "application/x-www-form-urlencoded";
userinfoRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
// gets the response
WebResponse userinfoResponse = await userinfoRequest.GetResponseAsync();
using (StreamReader userinfoResponseReader = new StreamReader(userinfoResponse.GetResponseStream()))
{
// reads response body
string userinfoResponseText = await userinfoResponseReader.ReadToEndAsync();
Log(userinfoResponseText);
}
}
/// <summary>
/// Appends the given string to the on-screen log, and the debug console.
/// </summary>
/// <param name="output">String to be logged</param>
private void Log(string output)
{
Console.WriteLine(output);
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name="length">Input length (nb. output will be longer)</param>
/// <returns></returns>
private static string GenerateRandomDataBase64url(uint length)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
rng.GetBytes(bytes);
return Base64UrlEncodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string, which is assumed to be ASCII.
/// </summary>
private static byte[] Sha256Ascii(string text)
{
byte[] bytes = Encoding.ASCII.GetBytes(text);
using (SHA256Managed sha256 = new SHA256Managed())
{
return sha256.ComputeHash(bytes);
}
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
private static string Base64UrlEncodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
// Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
// Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
// Hack to bring the Console window to front.
// ref: http://stackoverflow.com/a/12066376
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public void BringConsoleToFront()
{
SetForegroundWindow(GetConsoleWindow());
}
}
}
It sounds like an interesting project. I've not done this before, but here's how I would approach it...
The default template in Visual Studio for an ASP .NET Core Web Application is already a console application. Just check the Output Type field in the Application tab of the project's Properties window and it will show you the value Console.
So I would start with that project template. In the project creation wizard, change the Authentication to Individual User Accounts and you'll get an ASP .NET Core Web Application pretty much ready to implement your external login logic.
Then rip out all the files and code related to web applications. Change the Startup class so that it looks like a Startup for a normal console project and boom! You should have a bare-bones console app with external logins!
I'm trying to get Wopi host implementation in an ASP.NET MVC application.
Using this project
https://code.msdn.microsoft.com/office/Building-an-Office-Web-f98650d6
I don't get any calls hitting my API Controler
Discovery URL
<action name="view"
ext="docx"
default="true"
urlsrc="http://word-edit.officeapps.live.com/wv/wordviewerframe.aspx?<ui=UI_LLCC&><rs=DC_LLCC&><showpagestats=PERFSTATS&>" />
URL generated by my application
http://word-edit.officeapps.live.com/we/wordeditorframe.aspx?WOPISrc=http%3a%2f%2flocalhost%3a32876%2fapi%2fwopi%2ffiles%2ftest.docx&access_token=XskWxXK0Nro%3dhwYoiCFehrYAx85XQduYQHYQE9EEPC6EVgqaMiCp4%2bg%3d
I am using Local Host for testing purpose
Controller Route
[RoutePrefix("api/wopi")]
public class FilesController : ApiController
[Route("files/{name}/")]
public CheckFileInfo GetFileInfo(string name, string access_token)
{
Validate(name, access_token);
var fileInfo = _fileHelper.GetFileInfo(name);
bool updateEnabled = false;
if (bool.TryParse(WebConfigurationManager.AppSettings["updateEnabled"].ToString(), out updateEnabled))
{
fileInfo.SupportsUpdate = updateEnabled;
fileInfo.UserCanWrite = updateEnabled;
fileInfo.SupportsLocks = updateEnabled;
}
return fileInfo;
}
// GET api/<controller>/5
/// <summary>
/// Get a single file contents
/// </summary>
/// <param name="name">filename</param>
/// <returns>a file stream</returns>
[Route("files/{name}/contents")]
public HttpResponseMessage Get(string name, string access_token)
{
try
{
Validate(name, access_token);
var file = HostingEnvironment.MapPath("~/App_Data/" + name);
var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(file, FileMode.Open, FileAccess.Read);
responseMessage.Content = new StreamContent(stream);
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return responseMessage;
}
catch (Exception ex)
{
var errorResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError);
var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(ex.Message ?? ""));
errorResponseMessage.Content = new StreamContent(stream);
return errorResponseMessage;
}
}
It is not hitting to the API URL
WOPISrc cannot be 'localhost', it must be a link that office application server can access,
While you use office online application, then you need to provide a public ip or domain name of your wopi server instead of 'localhost'
I wrote a class that get the html from a site, this is the code:
public class NetworkHelper {
static Lazy<HttpClient> httpClient = new Lazy<HttpClient>(() => {
var handler = CreateHandler();
return new HttpClient(handler) {
Timeout = TimeSpan.FromSeconds(3)
};
});
static HttpMessageHandler CreateHandler() {
var handler = new HttpClientHandler();
// if the framework supports redirect configuration
// set max redirect to the desired amount the default is 50
if (handler.SupportsRedirectConfiguration) {
handler.AllowAutoRedirect = true;
handler.MaxAutomaticRedirections = 5;
}
// if the framework supports automatic decompression
// set automatic decompression
if (handler.SupportsAutomaticDecompression) {
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip |
System.Net.DecompressionMethods.Deflate;
}
return handler;
}
/// <summary>
/// Get the html structure of a site.
/// </summary>
/// <param name="url">Represents the URL of the page where to download the data.</param>
/// <returns>Return a string that contains the html of the site.</returns>
public async Task<string> GetHtmlAsync(Uri url, CancellationToken cancellationToken = default(CancellationToken)) {
var response = await httpClient.Value.GetAsync(url, cancellationToken);
var content = await response.Content.ReadAsStringAsync();
return content;
}
}
the problem's that when I try to download the data from this url:
string html = await new NetworkHelper().GetHtmlAsync(new Uri("https://int.soccerway.com/charts/statsplus/2139109"));
I'll get this content:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved here.</p>
</body></html>
so I guess AllowAutoRedirect isn't working as expected?
I discovered that changing:
return new HttpClient(handler) {
Timeout = TimeSpan.FromSeconds(3)
};
to:
return new HttpClient(handler) {
//Timeout = TimeSpan.FromSeconds(3)
};
all works well. Seems that there is a limited number of HTTP requests you can do per second, others get queued.
I want to know that how to generate signurl using google cloud storage classes in .net
I have created string as per the requirement
GET
1388534400
/bucket/objectname
but I now want to sign this url with p12 key and then want to make it url friendly
This library doesn't show specific function for it -> https://developers.google.com/resources/api-libraries/documentation/storage/v1/csharp/latest/annotated.html
So, basically I need .net alternate of Google_Signer_P12 class of php
$signer = new Google_Signer_P12(file_get_contents(__DIR__.'/'."final.p12"), "notasecret");
$signature = $signer->sign($to_sign);
Now there is a UrlSigner in the pre-release package Google.Cloud.Storage.V1 that can be used to to provide read-only access to existing objects:
// Create a signed URL which can be used to get a specific object for one hour.
UrlSigner urlSigner = UrlSigner.FromServiceAccountCredential(credential);
string url = urlSigner.Sign(
bucketName,
objectName,
TimeSpan.FromHours(1),
HttpMethod.Get);
Or write-only access to put specific object content into a bucket:
// Create a signed URL which allows the requester to PUT data with the text/plain content-type.
UrlSigner urlSigner = UrlSigner.FromServiceAccountCredential(credential);
var destination = "places/world.txt";
string url = urlSigner.Sign(
bucketName,
destination,
TimeSpan.FromHours(1),
HttpMethod.Put,
contentHeaders: new Dictionary<string, IEnumerable<string>> {
{ "Content-Type", new[] { "text/plain" } }
});
// Upload the content into the bucket using the signed URL.
string source = "world.txt";
ByteArrayContent content;
using (FileStream stream = File.OpenRead(source))
{
byte[] data = new byte[stream.Length];
stream.Read(data, 0, data.Length);
content = new ByteArrayContent(data)
{
Headers = { ContentType = new MediaTypeHeaderValue("text/plain") }
};
}
HttpResponseMessage response = await httpClient.PutAsync(url, content);
I know the question was for P12, but Google lead me here when I was looking to do this for the newer, preferred JSON method. I pieced this together with other samples and sites I found. Hope this help save some time.
public string GetSignedURL()
{
var myObj = "theObject";
var scopes = new string[] { "https://www.googleapis.com/auth/devstorage.read_write" };
var myBucket = "theBucket";
ServiceAccountCredential cred;
using ( var stream = new FileStream(#"\path to\private-key.json", FileMode.Open, FileAccess.Read) )
{
cred = GoogleCredential.FromStream(stream)
.CreateScoped(scopes)
.UnderlyingCredential as ServiceAccountCredential;
}
var urlSigner = UrlSigner.FromServiceAccountCredential(cred);
return urlSigner.Sign(myBucket, myObj, TimeSpan.FromHours(1), HttpMethod.Get);
}
A list of Scopes can be found here
The .NET client doesn't support signing URLs (it is an XML-only API), so you will need to either make a callout to a tool like gsutil or generate an RSA signature internal to your application (Signing and verifying signatures with RSA C#)
This is my google signer code, One can make it more dynamic as per their needs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Web;
using System.Security.Cryptography.X509Certificates;
namespace HHAFSGoogle
{
static class GoogleSigner
{
private static string hashAlgo = "SHA256";
public static string ServiceAccountEmail
{
get
{
return "XXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYY#developer.gserviceaccount.com";
}
}
public static string GoogleSecreat
{
get
{
return "notasecret";
}
}
public static string GoogleBucketDir
{
get
{
return "MyBucketDirectory";
}
}
public static string GoogleBucketName
{
get
{
return "MyBucket";
}
}
public static string CertiFilelocation
{
get
{
return System.Web.HttpContext.Current.Server.MapPath("p12file.p12");
}
}
/// <summary>
/// Get URL signature
/// </summary>
/// <param name="base64EncryptedData"></param>
/// <param name="certiFilelocation"></param>
/// <returns></returns>
public static string GetSignature(string base64EncryptedData, string certiFilelocation)
{
X509Certificate2 certificate = new X509Certificate2(certiFilelocation, GoogleSecreat, X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)certificate.PrivateKey;
RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
privateKey1.ImportParameters(csp.ExportParameters(true));
csp.ImportParameters(privateKey1.ExportParameters(true));
byte[] data = Encoding.UTF8.GetBytes(base64EncryptedData.Replace("\r", ""));
byte[] signature = privateKey1.SignData(data, hashAlgo);
bool isValid = privateKey1.VerifyData(data, hashAlgo, signature);
if (isValid)
{
return Convert.ToBase64String(signature);
}
else
{
return string.Empty;
}
}
/// <summary>
/// Get signed URL by Signature
/// </summary>
/// <param name="fileName"></param>
/// <param name="method"></param>
/// <param name="content_type"></param>
/// <param name="duration"></param>
/// <returns></returns>
public static string GetSignedURL(string fileName, string method = "GET", string content_type = "", int duration = 10)
{
TimeSpan span = (DateTime.UtcNow.AddMinutes(10) - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
var expires = Math.Round(span.TotalSeconds, 0);
// Encode filename, so URL characters like %20 for space could be handled properly in signature
fileName = HttpUtility.UrlPathEncode(fileName);
// Generate a string to sign
StringBuilder sbFileParam = new StringBuilder();
sbFileParam.AppendLine(method); //Could be GET, PUT, DELETE, POST
// /* Content-MD5 */ "\n" .
sbFileParam.AppendLine();
sbFileParam.AppendLine(content_type); // Type of content you would upload e.g. image/jpeg
sbFileParam.AppendLine(expires.ToString()); // Time when link should expire and shouldn't work longer
sbFileParam.Append("/" + GoogleBucketName + "/" + fileName);
var signature = System.Web.HttpContext.Current.Server.UrlEncode(GetSignature(sbFileParam.ToString(), CertiFilelocation));
return ("https://storage.googleapis.com/MyBucket/" + fileName +
"?response-content-disposition=attachment;&GoogleAccessId=" + ServiceAccountEmail +
"&Expires=" + expires + "&Signature=" + signature);
}
}
}
and to download file call above class to get signed url
GoogleSigner.GetSignedURL(bucketFileName)
and to upload file call above class to get signed url for upload url
GoogleSigner.GetSignedURL(fileName, "PUT", type);
I have a chunk of code that works using a HttpWebRequest and HttpWebResponse but I'd like to convert it to use HttpClient and HttpResponseMessage.
This is the chunk of code that works...
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceReq);
request.Method = "POST";
request.ContentType = "text/xml";
string xml = #"<?xml version=""1.0""?><root><login><user>flibble</user>" +
#"<pwd></pwd></login></root>";
request.ContentLength = xml.Length;
using (StreamWriter dataStream = new StreamWriter(request.GetRequestStream()))
{
dataStream.Write(xml);
dataStream.Close();
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
And this is the code that I'd like to replace it with, if only I could get it working.
/// <summary>
/// Validate the user credentials held as member variables
/// </summary>
/// <returns>True if the user credentials are valid, else false</returns>
public bool ValidateUser()
{
bool valid = false;
try
{
// Create the XML to be passed as the request
XElement root = BuildRequestXML("LOGON");
// Add the action to the service address
Uri serviceReq = new Uri(m_ServiceAddress + "?obj=LOGON");
// Create the client for the request to be sent from
using (HttpClient client = new HttpClient())
{
// Initalise a response object
HttpResponseMessage response = null;
// Create a content object for the request
HttpContent content = HttpContentExtensions.
CreateDataContract<XElement>(root);
// Make the request and retrieve the response
response = client.Post(serviceReq, content);
// Throw an exception if the response is not a 200 level response
response.EnsureStatusIsSuccessful();
// Retrieve the content of the response for processing
response.Content.LoadIntoBuffer();
// TODO: parse the response string for the required data
XElement retElement = response.Content.ReadAsXElement();
}
}
catch (Exception ex)
{
Log.WriteLine(Category.Serious,
"Unable to validate the Credentials", ex);
valid = false;
m_uid = string.Empty;
}
return valid;
}
I think the problem is creating the content object and the XML isn't being attached correctly (maybe).
The HttpClient.Post method has an overload that takes a contentType parameter, try this:
// Make the request and retrieve the response
response = client.Post(serviceReq, "text/xml", content);
I'd love to know the reason why the one approach doesn't work and the other does but I just don't have the time for any more digging. {:o(
Anyway, here's what I found.
A failure occurs when the content of the request is created using the following
HttpContent content = HttpContentExtensions.Create(root, Encoding.UTF8, "text/xml");
But it works correctly when you create the content like this...
HttpContent content = HttpContent.Create(root.ToString(), Encoding.UTF8, "text/xml");
The final working function is this:
/// <summary>
/// Validate the user credentials held as member variables
/// </summary>
/// <returns>True if the user credentials are valid, else false</returns>
public bool ValidateUser()
{
bool valid = false;
try
{
// Create the XML to be passed as the request
XElement root = BuildRequestXML("LOGON");
// Add the action to the service address
Uri serviceReq = new Uri(m_ServiceAddress + "?obj=LOGON");
// Create the client for the request to be sent from
using (HttpClient client = new HttpClient())
{
// Initalise a response object
HttpResponseMessage response = null;
#if DEBUG
// Force the request to use fiddler
client.TransportSettings.Proxy = new WebProxy("127.0.0.1", 8888);
#endif
// Create a content object for the request
HttpContent content = HttpContent.Create(root.ToString(), Encoding.UTF8, "text/xml");
// Make the request and retrieve the response
response = client.Post(serviceReq, content);
// Throw an exception if the response is not a 200 level response
response.EnsureStatusIsSuccessful();
// Retrieve the content of the response for processing
response.Content.LoadIntoBuffer();
// TODO: parse the response string for the required data
XElement retElement = response.Content.ReadAsXElement();
}
}
catch (Exception ex)
{
Log.WriteLine(Category.Serious, "Unable to validate the user credentials", ex);
valid = false;
m_uid = string.Empty;
}
return valid;
}
Thanks.