How to get file with resourcekey Google Drive API? - c#

I found here
https://stackoverflow.com/questions/68777408/how-to-obtain-a-resourcekey-using-google-drive-api
and
https://developers.google.com/drive/api/v3/resource-keys
I did get resourcekey of folder/file. But how to get copy file or folder with resourcekey?
I use code
DriveService.Files.Get(id).Execute()
It work update before. But now don't. I search many post but don't solve. Sorry my English not good. Thank for read.
Edit: I use C#.

You need to add the X-Goog-Drive-Resource-Keys header in your request. The simplest way to do that in the client libraries is via request interceptors. It's a little clunky, but not actually complicated:
public class HeaderExecuteInterceptor : IHttpExecuteInterceptor
{
private readonly string header;
private readonly string value;
public HeaderExecuteInterceptor(string header, string value)
{
this.header = header;
this.value = value;
}
public Task InterceptAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
request.Headers.Add(header, value);
// The value doesn't matter; Task.CompletedTask is simpler where supported.
return Task.FromResult(true);
}
}
// Where you make the request
const string ResourceKeysHeader = "X-Goog-Drive-Resource-Keys";
var request = service.Files.Get(id);
var interceptor = new HeaderExecuteInterceptor(ResourceKeysHeader, resourceKey);
request.AddExecuteInterceptor(interceptor);
var response = request.Execute();

Alternatively, anyone can use this code.
DriveService.HttpClient.DefaultRequestHeaders.Add("X-Goog-Drive-Resource-Keys", "ID/ResourceKey")
DriveService.Files.Get(id).Execute()
The "/" is required.

Related

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));
}

Correct use of Azure Durable Function - Serializing Complex Objects

So I'm prototyping some Azure Durable Functions, to try and understand to see if they will fit within a proposed solution for our internal API system.
Based on examples, I've created a Orchestrator Client (HelloOrchestratorClient.cs), that responds to a HttpTrigger. This client extracts some information from the original request, then proceeds to fire off a Orchestrator Function (HelloOrchestrator.cs) passing in some of the information extracted:
Complex HelloOrchestratorClient.cs:
[FunctionName("HttpSyncStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, methods: "get", Route = "orchestrators/{functionName}/wait")]
HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClient starter,
string functionName,
ILogger log)
{
HttpReq originalRequest = new HttpReq() {
DeveloperId = GetDevKey(req,apiHeaderKey),
QueryString = req.RequestUri.Query,
APIName = GetQueryStringValue(req,APIName),
APIVersion = GetQueryStringValue(req,APIVersion)
};
string instanceId = await starter.StartNewAsync(functionName, originalRequest);
TimeSpan timeout = GetTimeSpan(req, Timeout) ?? TimeSpan.FromSeconds(30);
TimeSpan retryInterval = GetTimeSpan(req, RetryInterval) ?? TimeSpan.FromSeconds(1);
return await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(
req,
instanceId,
timeout,
retryInterval);
}
The HelloOrchestrator.cs simply for now is just calling off to one of our internal API's and returning a JsonProduct payload (Simple POCO describing, you guessed it, a title), using a ActivityTigger named HelloOrchestrator.APICall to make the API call itself.
Complex HelloOrchestrator.cs:
[FunctionName("E1_JsonProduct")]
public static async Task<List<JsonProduct>> Run(
[OrchestrationTrigger] DurableOrchestrationContextBase context,
ILogger log)
{
List<JsonProduct> output = new List<JsonProduct>();
HttpReq r = context.GetInput<HttpReq>();
if(r != null)
{
if(r.DeveloperId == null)
{
return output;
}
output.Add(await context.CallActivityAsync<JsonProduct>("E1_CallAPI",r));
return output;
}
return output;
}
[FunctionName("E1_CallAPI")]
public async static Task<JsonProduct> APICall([ActivityTrigger] HttpReq req,
ILogger log)
{
JsonProduct products = null;
string u = $"{baseAddress}{req.APIVersion}/{req.APIName}{req.QueryString}";
var request = new HttpRequestMessage(HttpMethod.Get, u);
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")
);
request.Headers.Add("x-apikey",req.DeveloperId);
log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'.");
HttpResponseMessage response = await client.SendAsync(request);
// return await response.Content.ReadAsStringAsync();
if(response.IsSuccessStatusCode)
{
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = HelloProj.CosmosDB.Models.Products.Converter.Settings
};
products = await response.Content.ReadAsAsync<JsonProduct>(new [] {formatter});
}
return products;
}
Side Note: The plan is if I can get this to work, is to fan out a bunch of processes to different API's and fan back in again and merge the JSON payload and return it back to the originator.
Issue I'm experiencing
So, when my List<JsonProduct> is returned back from HelloOrchestrator.Run, I receive the following NullReferenceException found on this Gist (Big stack trace) and I receive a 500 response from the Orchestrator Client.
The following proves the output returned does actually have an object at runtime:
Could it be due to the complexity of JsonProduct (Again find the model classes here)? I ask, because when I swap out my Orchestrator Function for a simpler model structure, I don't receive a 500, I receive my JSON Payload.
This example shows the Simple Orchestrator Function HelloOrchestrator.cs, returning a simple TestToDo.cs (Gist for model) flat object that doesn't error:
Simple HelloOrchestrator.cs:
[FunctionName("E1_Todo")]
public static async Task<TestToDo> RunToDo(
[OrchestrationTrigger] DurableOrchestrationContextBase context,
ILogger log)
{
HttpReq r = context.GetInput<HttpReq>();
TestToDo todo = new TestToDo();
if(r != null)
{
todo = await context.CallActivityAsync<TestToDo>("E1_CallAPITodo",r);
}
return todo;
}
[FunctionName("E1_CallAPITodo")]
public async static Task<TestToDo> APITodoCall([ActivityTrigger] HttpReq req,
ILogger log)
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://jsonplaceholder.typicode.com/todos/1");
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")
);
log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'. for {req.QueryString}");
HttpResponseMessage response = await client.SendAsync(request);
return await response.Content.ReadAsAsync<TestToDo>();
}
More Information
If you require my full prototype projects, you can find them here:
Complex Project (Throws 500 and exception)
When you run it, use the following in something like Postman (After F5ing it):
http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N
Simple Project (No 500 or Exception thrown)
When you run it, use the following in something like Postman (after F5ing it):
http://localhost:7071/api/orchestrators/E1_Todo/wait?timeout=20&retryInterval=0.25
Looking at the callstack you posted, the NullReferenceException appears to be a bug in the DurableOrchestrationClient class. Looking at the code (which you can find here) is seems possible that if the query string you're using cannot be parsed correctly, a null-ref is possible.
You mentioned you're using the following URL for testing:
http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N
I wonder if the last two characters (&N) are the source of the problem. Is is possible to encode the & or remove it entirely to isolate the problem?
Either way, it would be great if you could log an issue here: https://github.com/Azure/azure-functions-durable-extension/issues

WebClient must not serve local files

I have a simple reverse proxy to avoid CORS in the browser.
In essence, it works like this:
string url = Request.QueryString["url"];
using (var webClient = new System.Net.WebClient())
{
byte[] buffer = webClient.DownloadData(url);
Response.Clear();
Response.BinaryWrite(buffer);
}
Usage:
/reverseproxy.aspx?url=http%3A%2F%2Fexample.com%2F
However, this has a vulnerability. The following request will return the logs of my IIS server.
/reverseproxy.aspx?url=c%3A%2Finetpub%2Flogs%2FLogFiles%2FW3SVC1%2Fu_ex170712.log
Is there a way to tell WebClient to not serve local files?
Without setting permissions and without using File.Exists(url)
You can create an extra assembly that creates your own implementation for a WebRequest and then configure your web.config to use your custom implementation for the file protocol.
Here is what you need:
Factory
This class decides if the reqest uri is local or not, add your own checks if needed.
public class NoLocalFile: IWebRequestCreate
{
public WebRequest Create(Uri uri)
{
// add your own extra checks here
// for example what Patrick offered in his answer
// I didn't test if I can't create a local UNC path
if (uri.IsFile && !uri.IsUnc)
{
// this is a local file request, we're going to return something safe by
// creating our own custom WebRequest
return (WebRequest) new NoLocalFileRequest();
}
else
{
// this should allow non local file request
// if needed
return FileWebRequest.Create(uri);
}
}
}
NoLocalFileRequest
This is the minimal implementation needed for our custom WebRequest
public class NoLocalFileRequest:WebRequest
{
// return an instance of our custom webResponse
public override WebResponse GetResponse()
{
return new NoLocalFileResponse();
}
}
NoLocalFileResponse
This class implements a WebResponse and returns a memorystream with the ASCII representation of the string "NO!". Here it is required to implement the ContentLength property as well.
public class NoLocalFileResponse : WebResponse {
static byte[] responseBytes = Encoding.ASCII.GetBytes("NO!");
public override long ContentLength
{
get {
return responseBytes.Length;
}
set {
// whatever
}
}
public override Stream GetResponseStream()
{
// what you want to return goes here
return new MemoryStream(responseBytes);
}
}
If you put these classes in a namespace called MyCustomWebRequests and compile this to an assembly called BlockLocalFile.dll, copy that assembly in the bin folder of your webapplication, then all you need is to make or add these changes to your web.config:
<system.net>
<webRequestModules>
<remove prefix="file:"/>
<add prefix="file:" type="MyCustomWebRequests.NoLocalFile, BlockLocalFile"/>
</webRequestModules>
</system.net>
When you testdrive this you should find that your previous code still works unchanged while the browsers that try the local file trick will get "NO!" returned instead of your file content.
Be aware that this config change applies to all code that runs in the appdomain of that webapplication. If you have legit code that also does require the normal use of the file protocol you have to make changes to the factory method that decides which WebRequest to create.
You could check if your provided URI is an URL or a file reference using the Uri class. You could use the code found here:
private static bool IsLocalPath(string p)
{
return new Uri(p).IsFile;
}
Even safer would be to check the scheme of the Uri to match to http or https:
private static bool IsHttpOrHttps(string uri)
{
Uri u = new Uri(uri);
return u.Scheme == Uri.UriSchemeHttp || u.Scheme == Uri.UriSchemeHttps;
}

Web API and HTTP Module

We have an HTTP Module that decodes all encoded requests.
It works great with all WCF requests, but NOT in Web Api requests- in Web Api the request (both POST and GET) gets to the service still encoded
I see that it Hits the HTTP Module but,again,still gets to the service encoded.
How can i fix it? or what am i doing wrong?
i know that its better to work with Message Handlers in Web Api, but HTTP Modules suppose to work too- no?
HTTP Module:
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += context_PreSendRequestContent;
}
void context_PreSendRequestContent(object sender, EventArgs e)
{
string encodedQuerystring = HttpContext.Current.Request.QueryString.ToString();
if (!string.IsNullOrEmpty(encodedQuerystring))
{
System.Collections.Specialized.NameValueCollection col = new System.Collections.Specialized.NameValueCollection();
col.Add("q", encodedQuerystring);
WebFunction.CreateQuerystring(HttpContext.Current, col);
}
}
void context_BeginRequest(object sender, EventArgs e)
{
string encodedQueryString = String.Empty;
if (HttpContext.Current.Request.QueryString.Count > 0 && HttpContext.Current.Request.QueryString["q"] != null)
{
object _p = HttpContext.Current.Request.QueryString;
encodedQueryString = HttpContext.Current.Server.UrlDecode(HttpContext.Current.Request.QueryString["q"].ToString());
string originalQueryString = HttpContext.Current.Server.UrlDecode(WebFunction.Base64Decode(encodedQueryString));
if (!string.IsNullOrEmpty(originalQueryString))
{
WebFunction.CreateQuerystring(HttpContext.Current, WebFunction.ConvertQueryToCollection(originalQueryString));
}
}
}
WebFunction:
public static void CreateQuerystring(HttpContext context, System.Collections.Specialized.NameValueCollection nameValueCollection)
{
// reflect to readonly property
PropertyInfo isreadonly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isreadonly.SetValue(context.Request.QueryString, false, null);
context.Request.QueryString.Clear();
context.Request.QueryString.Add(nameValueCollection);
// make collection readonly again
isreadonly.SetValue(context.Request.QueryString, true, null);
}
Web Api:
public class NamesController : ApiController
{
[HttpGet]
[ActionName("GET_NAMES")]
public Drugs_ResponseData Get(string q)
{
//need to add the decode function to get it to work
string[] arrAmpersant = Commonnn.DecodeFrom64(q).Split('&');
Names_obj = new Names();
return _obj.GetResult(Convert.ToInt32(Commonnn.GetValFromEqual(arrAmpersant[0])));
}
}
It seems that the Web API doesn't use the QueryString collection in the request, but it parses the URL itself.
See the GetQueryNameValuePairs method in this file - they take the Uri and parse its Query property.
So you have two options to do that:
The dirty one is to change the Uri of the request in your the HTTP module. I don't know whether it's possible, but some reflection could do the trick.
The nicer way would be to use the Web API message handler.
May I suggest you use Context.Items and let the QueryString have the encoded version.
It's a not very well known built in key/value dictionary which last throughout a request where you easily store any object and then share it between module, handlers, etc.
Using this would very like give you a better performance than unlocking the QueryString object, but more importantly, you process the value in one place and reuse it in many, and when needed, you just add a second value, the complete QueryString collection or any other value you want to share across a request.
void context_BeginRequest(object sender, EventArgs e)
{
string encodedQueryString = String.Empty;
if (HttpContext.Current.Request.QueryString.Count > 0 && HttpContext.Current.Request.QueryString["q"] != null)
{
string encodedQueryString = HttpContext.Current.Server.UrlDecode(HttpContext.Current.Request.QueryString["q"].ToString());
HttpContext.Current.Items("qs_d") = HttpContext.Current.Server.UrlDecode(WebFunction.Base64Decode(encodedQueryString));
}
}
Web Api:
public class NamesController : ApiController
{
[HttpGet]
[ActionName("GET_NAMES")]
public Drugs_ResponseData Get(string q)
{
string[] arrAmpersant = Commonnn.DecodeFrom64(HttpContext.Current.Items("qs_d").ToString()).Split('&');
Names_obj = new Names();
return _obj.GetResult(Convert.ToInt32(Commonnn.GetValFromEqual(arrAmpersant[0])));
}
}
Side note: I see you call HttpContext.Current.Server.UrlDecode twice. I don't think you need that unless your Base64Decode method encode the value again.
You can handle like this
protected void Application_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
string path = app.Context.Request.Url.PathAndQuery;
int pos = path.IndexOf("?");
if (pos > -1)
{
string[] array = path.Split('?');
app.Context.RewritePath(array[0]+"?"+ HttpContext.Current.Server.UrlDecode(array[1]));
}
}
Adding on to #Tomáš Herceg 's answer, I would implement a Web Api message handler rather than modifying your HttpModule to accommodate Web Api.
public class DecodeQueryStringMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Get)
{
var originalQueryString = request.RequestUri.Query;
if (!string.IsNullOrEmpty(originalQueryString))
{
var ub = new UriBuilder(request.RequestUri) { Query = HttpUtility.UrlDecode(originalQueryString) };
request.RequestUri = ub.Uri;
}
}
return base.SendAsync(request, cancellationToken);
}
}
It is possible, but you will need reflection, what means that exist a risk here. Please, let me suggest you what I consider to be a more clean solution after the solution.
Solution
if (!string.IsNullOrEmpty(originalQueryString))
{
var request = HttpContext.Current.Request;
request.GetType().InvokeMember("QueryStringText", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, request, new[] { "q=" + originalQueryString });
//WebFunction.CreateQuerystring(HttpContext.Current, WebFunction.ConvertQueryToCollection(originalQueryString));
}
This will update the following properties of the Request:
Request.Param
Request.QueryString
Request.ServerVariables
Request.Url
but will not update the:
Request.RawUrl
Cleaner Solution
IIS URL Rewrite Module
http://www.iis.net/learn/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module

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