Connecting to cloud using C# WebDav

Hello, guys!

I am quite new to this NextCloud concepts and connecting it with other applications. That’s why I wanted to ask you one thing I couldn’t solve myself. I wanted to estabilish a connection between C# App and NextCloud using WebDav but somehow it doesn’t work.

I have downloaded this NuGET package to my Visual Studio -

I used the code as in my C# app as follows:

        var client = new WebDAVClient.Client(new NetworkCredential { UserName = "usernamehere", Password = "passwordhere" });
        client.Server = "https://cd.wedos.com/";
        client.BasePath = "/remote.php/dav/files/usernamehere/";

        var files = await client.List();

I have also generated my new password for my new profile in NextCloud and ‘‘usernamehere’’ and ‘‘passwordhere’’ are replaced in my code.

Error: InvalidOperationException: Nullable object must have a value.

I tried to debug it in visual studio to see if there is any value assigned to client, but there wasn’t. Why?

I copied the WebDav link from here:
image

Thank you for your help!

did you try a different webdav client to make sure that everything on Nextcloud is working properly?

You don’t have to connect at some point first?

1 Like

Well, I tried connecting from Win10 using that link and it requires username and password… once it’s inserted, I have all access to my cloud from my PC. Other methods to connect to my cloud (for example using my c# code) doesn’t work.

did you try a different webdav client ---- you mean like downloading different NuGET package in Visual Studio?

No, the native windows client would be a different one already.

Someone managed to connect with a slightly changed Server and BasePath String:

I tried connecting, but there is an error with my code. It seems to be finally connecting to my cloud 'cause it’s not null already… but this time, when I tried to connect and find some files, another exception pops up

ArgumentException: Only HTTP/1.0 and HTTP/1.1 version requests are currently supported.

I know there’s a problem with packet headers, but I don’t know how to fix this myself.

Code:

        // Specify the user credentials and use it to create a WebDavSession instance.
        var credentials = new NetworkCredential("username", "pass");
        var webDavSession = new WebDavSession(@"https://cd.wedos.com/remote.php/dav/files/username/", credentials);
        var items = await webDavSession.ListAsync(@"Talk/");

https://decatec.de/ext/PortableWebDAVLibrary/Doc/html/379fbde2-659c-4cd5-9eb6-0850cd041621.htm

You probably can disable http/2.0 temporarily to check if that works. But shouldn’t it be backward compatible for http/1.1 clients?

I have looked it up (the compatibility between HTTP/2.0 and HTTP/1.0 and there is no way these two are compatible according to many threads. The client-server would not be able to communicate which is very strange… thuought the compatibility wouldn’t be the problem from higher version to lower.

Can I ask you how can I disable it? Is it somehow done in my C# code or in my operating system?

No, the http would be on the server side (apache or nginx)

The problem is I have my Cloud server on public webhosting.

Hi, I can confirm that the webdav client works with C# :).

I’d suggest building a HttpClient and injecting that to the constructor (should be an option).

You mean the first one I started the topic with? The one that said nullable object error?

Can I ask you for an advice how to do it? I mean, injecting HttpClient to constructor.

Sure!

string username = "";
string password = "<app_password>";
String encoded = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password));

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://cloud.nextcloud.com/");
client.DefaultRequestHeaders.Add("OCS-APIRequest", "true");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", encoded);

You pass the HttpClient:

public class WebDav
{            
private IWebDavClient _webDavClient;
            private string _username;
            public WebDav(HttpClient client, string username)
            {
                _username = username;
                _webDavClient = new WebDavClient(client);
            }

        public async Task<PropfindResponse> GetItemsAsync()
        {
            return await _webDavClient.Propfind($"remote.php/dav/files/{_username}");
        }
}
2 Likes

My God, thank you! I have one more question for you, though - by app password you mean password to my cloud? Because I think what I am passing in to connect to Cloud isn’t API requests… but rather Authentication Credentials… or is it the same here?

The authentication for either api requests and/or webdav is the same. You can generate an app password manually under Settings (user) > Security > bottom of ‘Devices & sessions’.

I am not entirely positive if you can authenticate with just your password. You can easily test it though with the given sample.

Where did you take WebDav function from? It’s missing a return value as I see. The upper part of the code works well and I quite understand it. I used it to GET something from HTTP URL… It’s quite similar. But the second part… I don’ get it… Do you have any package you are using, or what?

The WebDav function is the constructor of my own class. E.g. could also be ‘public WebDav’ > ‘public ExampleTomas’, as long your class shares the same name.

The first code example creates the HttpClient, you can use this for either api requests or webdav functionality.

The second part entirely implements the webdav library you also referenced to, I edited the code a bit to add a class around it.

Okay, it seems to be working now because I created a user in my Nextcloud and after I started up my application, it says:

Last activity: a few seconds ago…

I have still one question… how can I find a certain name of the file and then download using that code? If you would be so kind.

The example on their git page also explains it… but generally…

Extend this from:

  public async Task<PropfindResponse> GetItemsAsync()
        {
            return await _webDavClient.Propfind($"remote.php/dav/files/{_username}");
        }

To:

  public async Task<PropfindResponse> GetItemsAsync(string path)
        {
            return await _webDavClient.Propfind($"remote.php/dav/files/{_username}/{path}");
        }

In this instance ‘path’ is literally the path where the file can be found. e.g. //folder1/subfolder2 (note: no / at end).

// Retrieve list of items in 'Test' folder
var folderFiles = await GetItemsAsync("path/path2/subpath");
// Find first file in 'Test' folder
var folderFile = folderFiles.FirstOrDefault(f => f.IsCollection == false);

var tempFileName = Path.GetTempFileName();

// Download item into a temporary file
using (var tempFile = File.OpenWrite(tempFileName))
using (var stream = await client.Download(folderFile.Href))
	await stream.CopyToAsync(tempFile);
2 Likes

Alright, thank you for all your time and answers! I have marked your answer as solution. Have a nice day!

Hi, man! One more question to ask… The code you have copied hre recently is kinda incorrect because the FirstOrDefault writes an error… And on the other hand, I would like to download the whole folder, not its content one by one, if you understand me… Is this possbile? If yes, can you direct me to right path so I don’t go in circles? Would be much appriciated, thanks!