[WebDav] WebDav基础知识

文章目录

  • 什么是WebDav
  • WebDav常用命令
  • WebDav常用命令的测试(代码)
    • PROPFIND 方法测试
    • PUT 方法测试
    • GET 方法测试
    • PROPPATCH方法
  • WebDav缓存
    • Cache-Control
    • Etag
      • 测试
    • 强制重新验证
    • 不需要缓存
  • WebDav的锁
  • WebDav的状态码
  • WebDav身份验证
  • WebDav版本控制
  • WebDav和FTP的区别
  • 参考

什么是WebDav

What is WebDAV?
Briefly: WebDAV stands for “Web-based Distributed Authoring and Versioning”. It is a set of extensions to the HTTP protocol which allows users to collaboratively edit and manage files on remote web servers.
WebDAV Resources

WebDav是基于HTTP的协议,他可以允许客户端远程编辑Web内容。

WebDAV的特性和优势
支持创建、修改、复制、移动、移除、查询、列举文件
文件锁
版本控制
支持修改文件属性
安全完善的身份验证机制
支持https加密
支持proxy
客户端缓存
方便的客户端工具:和局域网中的文件共享一样简单使用。
来源:学习WebDav

WebDav常用命令

WebDav在HTTP的基础上扩展了自己的命令,例如:
PROPFIND 用于获取文件夹列表、文件夹内的文件列表、文件夹和文件的属性;
MKCOL 用于创建空文件夹;
PUT 用于上传文件;
GET 用于下载文件;
COPY 用于复制文件;
MOVE 用于移动文件;

WebDav常用命令的测试(代码)

我在坚果云网盘中,创建了几个文件夹,上传了几个文件。并按照如何在Zotero中设置webdav连接到坚果云?进行了网盘的WebDav服务配置,生成了WebDav密码。
在这里插入图片描述
根据学习WebDav ,直接在windows cmd使用curl命令就可以一定程度测试WebDav,我这里是在VS 2022中,通过libcurl库,向坚果云发送请求。
关于VS中如何导入libcurl库,可以看[libcurl] windows visual studio 导入libcurl库。

PROPFIND 方法测试

代码:

#include <curl/curl.h>
#include <iostream>
#include <fstream>

using std::cout;
using std::endl;
using std::ios;

#define ERROR(X) (cout << __FUNCDNAME__ <<  " " << (X) << " " << "error" << endl, -1)
#define ERROR2(X,Y) (cout << __FUNCDNAME__ <<  " " << (X) << " " << (Y) << " " << "error" << endl, -1)

#if 1 // WebDav
size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
int My_PROPFIND();
FILE* fp;

int main()
{
	//打开一个文件,用于输出WebDav响应
	char filename[256];
	sprintf_s(filename, 256, "%s.%s", "WebDav-Test", "xml");
	errno_t err = fopen_s(&fp, filename, "wb");
	if (err)
		return ERROR2("fopen_s", err);

	//初始化curl
	curl_global_init(CURL_GLOBAL_WIN32);

	//WebDav请求函数
	My_PROPFIND();

	curl_global_cleanup();
	cout << "program end." << endl;
}

int My_PROPFIND()
{
	const char* host = "https://dav.jianguoyun.com";
	const char* url = "https://dav.jianguoyun.com/dav/box1";
	
	CURL* curl = curl_easy_init();
	if (curl) {
		//设置HTTP头		
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND"); //修改HTTP方法
		curl_easy_setopt(curl, CURLOPT_URL, url); //设置URL		
		curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_1_1); //指定HTTP版本
		curl_easy_setopt(curl, CURLOPT_USERNAME, "这里隐藏掉邮箱地址@qq.com"); //设置访问WebDav账号和密码
		curl_easy_setopt(curl, CURLOPT_PASSWORD, "axs5pyhc2j6n7q");
		struct curl_slist* list = NULL; //设置HTTP头部字段
		list = curl_slist_append(list, "Connection: close"); //不要长连接
		list = curl_slist_append(list, "Accept: */*");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
		

		//指定用于SSL证书验证的证书
		CURLcode err = curl_easy_setopt(curl, CURLOPT_CAINFO, "D:\\SourceCode\\cert\\_.jianguoyun.com.crt");
		if (err != CURLE_OK) {
			cout << "CURLOPT_CAPATH err:" << err << endl;
		}
		//如果不设置,会出现:unable to get local issuer certificate的错误

		//设定HTTP响应的处理方法
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)fp);
		//设定控制台回显调试信息
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		//执行HTTP请求
		CURLcode ret = curl_easy_perform(curl);
		if (ret != CURLE_OK) {
			curl_easy_cleanup(curl);
			fclose(fp);
			return ERROR2("curl_easy_perform", ret);
		}
	}
	else {
		fclose(fp);
		return ERROR("curl_easy_init");
	}
	fclose(fp);
	curl_easy_cleanup(curl);
	return 0;
}

size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
	int realsize = size * nmemb;
	fwrite(ptr, 1, realsize, fp);
	return realsize;
}
#endif

控制台输出:

* Host dav.jianguoyun.com:443 was resolved.
* IPv6: (none)
* IPv4: 36.155.116.36, 36.155.116.35
*   Trying 36.155.116.36:443...
* Connected to dav.jianguoyun.com (36.155.116.36) port 443
* ALPN: curl offers http/1.1
*  CAfile: D:\SourceCode\cert\_.jianguoyun.com.crt
*  CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 / [blank] / UNDEF
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: CN=*.jianguoyun.com
*  start date: Jan 23 00:00:00 2024 GMT
*  expire date: Feb 19 23:59:59 2025 GMT
*  subjectAltName: host "dav.jianguoyun.com" matched cert's "*.jianguoyun.com"
*  issuer: C=GB; ST=Greater Manchester; L=Salford; O=Sectigo Limited; CN=Sectigo RSA Domain Validation Secure Server CA
*  SSL certificate verify ok.
*   Certificate level 0: Public key type ? (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/1.x
* Server auth using Basic with user ‘这里隐藏掉邮箱地址@qq.com'
> PROPFIND /dav/box1 HTTP/1.1
Host: dav.jianguoyun.com
Authorization: Basic MjgwMjAzNzEyN这里隐藏掉B5aGMyajZuN3F0eg==
Connection: close
Accept: */*

< HTTP/1.1 207 Multi-Status
< Server: nginx
< Date: Mon, 19 Feb 2024 04:14:58 GMT
< Content-Type: text/xml; charset=UTF-8
< Content-Length: 2882
< Connection: close
< Pragma: no-cache
< Cache-Control: no-cache
<
* Closing connection
program end.

可以看到,发出的请求是:

> PROPFIND /dav/box1 HTTP/1.1
Host: dav.jianguoyun.com
Authorization: Basic MjgwMjAzNzEyN这里隐藏掉B5aGMyajZuN3F0eg==
Connection: close
Accept: */*

收到的响应HTTP头是:

< HTTP/1.1 207 Multi-Status
< Server: nginx
< Date: Mon, 19 Feb 2024 04:14:58 GMT
< Content-Type: text/xml; charset=UTF-8
< Content-Length: 2882
< Connection: close
< Pragma: no-cache
< Cache-Control: no-cache

输出到文件中的XML内容是:

<d:multistatus>
<d:response>
 <d:href>/dav/box1/</d:href>
<d:propstat>
<d:prop>
 <d:getcontenttype>httpd/unix-directory</d:getcontenttype>
 <d:displayname>box1</d:displayname>
 <d:owner>这里隐藏掉邮箱地址@qq.com</d:owner>
<d:resourcetype>
 <d:collection/>
 </d:resourcetype>
 <d:getcontentlength>0</d:getcontentlength>
 <d:getlastmodified>Mon, 19 Feb 2024 04:14:58 GMT</d:getlastmodified>
<d:current-user-privilege-set>
<d:privilege>
 <d:read/>
 </d:privilege>
<d:privilege>
 <d:write/>
 </d:privilege>
<d:privilege>
 <d:all/>
 </d:privilege>
<d:privilege>
 <d:read_acl/>
 </d:privilege>
<d:privilege>
 <d:write_acl/>
 </d:privilege>
 </d:current-user-privilege-set>
 </d:prop>
 <d:status>HTTP/1.1 200 OK</d:status>
 </d:propstat>
 </d:response>
<d:response>
 <d:href>/dav/box1/WeatherWS.xml</d:href>
<d:propstat>
<d:prop>
 <d:getetag>UsZ7ybf73r39UXEEPQs5qA</d:getetag>
 <d:getcontenttype>text/xml</d:getcontenttype>
 <d:displayname>WeatherWS.xml</d:displayname>
 <d:owner>这里隐藏掉邮箱地址@qq.com</d:owner>
 <d:getcontentlength>29712</d:getcontentlength>
 <d:getlastmodified>Fri, 29 Dec 2023 09:02:10 GMT</d:getlastmodified>
 <d:resourcetype/>
<d:current-user-privilege-set>
<d:privilege>
 <d:read/>
 </d:privilege>
<d:privilege>
 <d:write/>
 </d:privilege>
<d:privilege>
 <d:all/>
 </d:privilege>
<d:privilege>
 <d:read_acl/>
 </d:privilege>
<d:privilege>
 <d:write_acl/>
 </d:privilege>
 </d:current-user-privilege-set>
 </d:prop>
 <d:status>HTTP/1.1 200 OK</d:status>
 </d:propstat>
 </d:response>
<d:response>
 <d:href>/dav/box1/box1_1</d:href>
<d:propstat>
<d:prop>
 <d:getetag/>
 <d:getcontenttype>httpd/unix-directory</d:getcontenttype>
 <d:displayname>box1_1</d:displayname>
 <d:owner>这里隐藏掉邮箱地址@qq.com</d:owner>
 <d:getcontentlength>0</d:getcontentlength>
 <d:getlastmodified>Tue, 13 Feb 2024 04:29:51 GMT</d:getlastmodified>
<d:resourcetype>
 <d:collection/>
 </d:resourcetype>
<d:current-user-privilege-set>
<d:privilege>
 <d:read/>
 </d:privilege>
<d:privilege>
 <d:write/>
 </d:privilege>
<d:privilege>
 <d:all/>
 </d:privilege>
<d:privilege>
 <d:read_acl/>
 </d:privilege>
<d:privilege>
 <d:write_acl/>
 </d:privilege>
 </d:current-user-privilege-set>
 </d:prop>
 <d:status>HTTP/1.1 200 OK</d:status>
 </d:propstat>
 </d:response>
<d:response>
 <d:href>/dav/box1/box1file.pdf</d:href>
<d:propstat>
<d:prop>
 <d:getetag>rlLyz4SUXar-UNmip-F5Qw</d:getetag>
 <d:getcontenttype>application/pdf</d:getcontenttype>
 <d:displayname>box1file.pdf</d:displayname>
 <d:owner>这里隐藏掉邮箱地址@qq.com</d:owner>
 <d:getcontentlength>2422816</d:getcontentlength>
 <d:getlastmodified>Wed, 15 Nov 2023 08:43:08 GMT</d:getlastmodified>
 <d:resourcetype/>
<d:current-user-privilege-set>
<d:privilege>
 <d:read/>
 </d:privilege>
<d:privilege>
 <d:write/>
 </d:privilege>
<d:privilege>
 <d:all/>
 </d:privilege>
<d:privilege>
 <d:read_acl/>
 </d:privilege>
<d:privilege>
 <d:write_acl/>
 </d:privilege>
 </d:current-user-privilege-set>
 </d:prop>
 <d:status>HTTP/1.1 200 OK</d:status>
 </d:propstat>
 </d:response>
 </d:multistatus>

我对Dav中的box1文件夹发送了PROPDIND请求,在响应回来的XML内容中,列出了box1中的每个文件夹和文件(包括box1自己)。每个<d:response>节点都包含了一个文件夹或者文件,<d:response>节点,是文件夹或者文件的属性信息。

PUT 方法测试

只保留方法部分,其余代码省略。

int My_PUT()
{
	//这里需要指明需要在Dav上创建的文件的路径“box1”和名字“Upload_test.txt”
	const char* url = "https://dav.jianguoyun.com/dav/box1/Upload_test.txt";
	CURL* curl = curl_easy_init();
	if (curl) {		
		//设置HTTP头		
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); //修改HTTP方法
		curl_easy_setopt(curl, CURLOPT_URL, url); //设置URL		
		curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_1_1); //指定HTTP版本
		curl_easy_setopt(curl, CURLOPT_USERNAME, "2xxxxxxxx4@qq.com"); //设置访问WebDav账号和密码
		curl_easy_setopt(curl, CURLOPT_PASSWORD, "axs5pyhc2j6n7q");
		struct curl_slist* list = NULL; //设置HTTP头部字段
		list = curl_slist_append(list, "Connection: close"); //不要长连接
		list = curl_slist_append(list, "Accept: */*");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);

		//设置要上传的文件信息
		//打开文件
		curl_off_t fsize = 0;
		FILE* src = nullptr;
		errno_t ferr = fopen_s(&src, "D:\\SourceCode\\TransFILE1.txt", "rb");
		if (ferr)
			return -1;
		//获取文件大小
		fseek(src, 0, SEEK_END);	
		fsize = ftell(src);
		fseek(src, 0, SEEK_SET);
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); //设置读取文件的回调函数
		curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); //启动Upload服务
		curl_easy_setopt(curl, CURLOPT_READDATA, src);//设置传入回调函数的文件句柄
		curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize);//设置文件大小

		//指定用于SSL证书验证的证书
		CURLcode err = curl_easy_setopt(curl, CURLOPT_CAINFO, "D:\\SourceCode\\cert\\_.jianguoyun.com.crt");
		if (err != CURLE_OK) {
			cout << "CURLOPT_CAPATH err:" << err << endl;
		}
		//如果不设置,会出现:unable to get local issuer certificate的错误

		//设定HTTP响应的处理方法
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)fp);
		//设定控制台回显调试信息
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		//执行HTTP请求
		CURLcode ret = curl_easy_perform(curl);
		if (ret != CURLE_OK) {
			curl_easy_cleanup(curl);
			fclose(fp);
			return ERROR2("curl_easy_perform", ret);
		}
	}
	else {
		fclose(fp);
		return ERROR("curl_easy_init");
	}
	fclose(fp);
	curl_easy_cleanup(curl);
	return 0;
}
static size_t read_cb(char* ptr, size_t size, size_t nmemb, void* userdata)
{
	FILE* src = (FILE*)userdata;
	/* copy as much data as possible into the 'ptr' buffer, but no more than
	   'size' * 'nmemb' bytes */
	size_t retcode = fread(ptr, size, nmemb, src);

	return retcode;
}

控制台回显信息(部分):

> PUT /dav/box1/Upload_test.txt HTTP/1.1
Host: dav.jianguoyun.com
Authorization: Basic MjgwMjAzNzEyNEB---------------aGMyajZuN3F0eg==
Connection: close
Accept: */*
Content-Length: 1844

* We are completely uploaded and fine
< HTTP/1.1 204 No Content
< Server: nginx
< Date: Mon, 19 Feb 2024 05:59:17 GMT
< Connection: close
< X-File-Version: 3
< Pragma: no-cache
< Cache-Control: no-cache
<
* Closing connection

WebDav查看:
在这里插入图片描述

GET 方法测试

int My_GET()
{
	const char* url = "https://dav.jianguoyun.com/dav/box1/Upload_test.txt";

	CURL* curl = curl_easy_init();
	if (curl) {
		//设置HTTP头		
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); //修改HTTP方法
		curl_easy_setopt(curl, CURLOPT_URL, url); //设置URL		
		curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_1_1); //指定HTTP版本
		curl_easy_setopt(curl, CURLOPT_USERNAME, "2--------4@qq.com"); //设置访问WebDav账号和密码
		curl_easy_setopt(curl, CURLOPT_PASSWORD, "axs5pyhc2j6n7q");
		struct curl_slist* list = NULL; //设置HTTP头部字段
		list = curl_slist_append(list, "Connection: close"); //不要长连接
		list = curl_slist_append(list, "Accept: */*");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);

		//指定用于SSL证书验证的证书
		CURLcode err = curl_easy_setopt(curl, CURLOPT_CAINFO, "D:\\SourceCode\\cert\\_.jianguoyun.com.crt");
		if (err != CURLE_OK) {
			cout << "CURLOPT_CAPATH err:" << err << endl;
		}
		//如果不设置,会出现:unable to get local issuer certificate的错误

		//设定HTTP响应的处理方法
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)fp);
		//设定控制台回显调试信息
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		//执行HTTP请求
		CURLcode ret = curl_easy_perform(curl);
		if (ret != CURLE_OK) {
			curl_easy_cleanup(curl);
			fclose(fp);
			return ERROR2("curl_easy_perform", ret);
		}
	}
	else {
		fclose(fp);
		return ERROR("curl_easy_init");
	}
	fclose(fp);
	curl_easy_cleanup(curl);
	return 0;
}

控制台回显结果(部分):

> GET /dav/box1/Upload_test.txt HTTP/1.1
Host: dav.jianguoyun.com
Authorization: Basic MjgwMjAzNzEyNEBxc--------------GMyajZuN3F0eg==
Connection: close
Accept: */*

< HTTP/1.1 200 OK
< Server: nginx
< Date: Mon, 19 Feb 2024 06:13:18 GMT
< Content-Type: text/plain
< Content-Length: 1844
< Connection: close
< Etag: 8sKBsnMc5tH71U67xjQTCQ
< Pragma: public
< Cache-Control: max-age=5
< Content-Disposition: attachment
<
* Closing connection
program end.

下载的文件内容保存在以下代码绑定的文件中了:

		//设定HTTP响应的处理方法
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)fp);

PROPPATCH方法

PROPPATCH方法用于修改文件的属性。
WebDav方法的HTTP Body是XML格式,前面尝试的几个请求都没有添加Body。
PROPPATCH需要在Body中添加需要修改的属性指令。

以刚才PUT的文件Upload_test.txt为目标,把它的<d:displayname>修改为Upload_test_1.txt。
但是没有效果,坚果云给的响应中消息中,也没有显示失败信息。

我咨询了坚果云的客服,客服联系技术给出了回复,目前坚果云不支持PROPPATCH方法
因此,无法验证我的代码是否正确,但是还是记录一下代码,期待以后有机会验证。

代码:

int My_PROPPATCH()
{
	const char* url = "https://dav.jianguoyun.com/dav/box1/Upload_test.txt";

	CURL* curl = curl_easy_init();
	if (curl) {
		//设置HTTP头		
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); //修改HTTP方法
		curl_easy_setopt(curl, CURLOPT_URL, url); //设置URL		
		curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_1_1); //指定HTTP版本
		curl_easy_setopt(curl, CURLOPT_USERNAME, "2802037124@qq.com"); //设置访问WebDav账号和密码
		curl_easy_setopt(curl, CURLOPT_PASSWORD, "axs5pyhc2j6n7qtz");
		struct curl_slist* list = NULL; //设置HTTP头部字段
		//list = curl_slist_append(list, "Connection: close"); //不要长连接
		list = curl_slist_append(list, "Accept: */*");
		list = curl_slist_append(list, "Content-Type:application/xml; charset= 'utf-8'");
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);

		//指定用于SSL证书验证的证书
		CURLcode err = curl_easy_setopt(curl, CURLOPT_CAINFO, "D:\\SourceCode\\cert\\_.jianguoyun.com.crt");
		if (err != CURLE_OK) {
			cout << "CURLOPT_CAPATH err:" << err << endl;
		}
		//如果不设置,会出现:unable to get local issuer certificate的错误

		//设定HTTP响应的处理方法
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)fp);
		//设定控制台回显调试信息
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		//使用TinyXML库,添加Http Body
		TiXmlDocument* tinyXmlDoc = new TiXmlDocument();

		TiXmlDeclaration* tinyXmlDeclare = new TiXmlDeclaration("1.0", "utf-8", "");  // xml的声明
		tinyXmlDoc->LinkEndChild(tinyXmlDeclare);

		TiXmlElement* Library = new TiXmlElement("D:propertyupdate");
		Library->SetAttribute(" xmlns:D", "DAV");
		Library->SetAttribute(" xmlns:S", "http://ns.jianguoyun.com");
		tinyXmlDoc->LinkEndChild(Library);	

		TiXmlElement* Set = new TiXmlElement("D:set");
		Library->LinkEndChild(Set);

		TiXmlElement* Prop = new TiXmlElement("D:prop");
		Set->LinkEndChild(Prop);

		TiXmlElement* Displayname2 = new TiXmlElement("S:publish");
		TiXmlText* newname = new TiXmlText("Upload_test_1.txt");	
		Displayname2->LinkEndChild(newname);	
		Prop->LinkEndChild(Displayname2);

		TiXmlPrinter printer;
		tinyXmlDoc->Accept(&printer);
		printf("%s\n", printer.CStr());
		char body[1024] = { 0x00 };
		strcpy_s(body, (rsize_t)1024, printer.CStr());
		curl_off_t size = strlen(body);
		
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb_patch); //设置读取文件的回调函数
		curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); //启动Upload服务
		curl_easy_setopt(curl, CURLOPT_READDATA, body);//设置传入回调函数的文件句柄
		curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)size);//设置文件大小

		//执行HTTP请求
		CURLcode ret = curl_easy_perform(curl);
		if (ret != CURLE_OK) {
			curl_easy_cleanup(curl);
			/*fclose(fp);*/
			return ERROR2("curl_easy_perform", ret);
		}
	}
	else {
		//fclose(fp);
		return ERROR("curl_easy_init");
	}
	//fclose(fp);
	curl_easy_cleanup(curl);
	return 0;
}

控制台回显:

> PROPPATCH /dav/box1/Upload_test.txt HTTP/1.1
Host: dav.jianguoyun.com
Authorization: Basic MjgwMjAzNzEyNEBxcS5jb206YXhzNXB5aGMyajZuN3F0eg==
Accept: */*
Content-Type:application/xml; charset= 'utf-8'
Content-Length: 243

* We are completely uploaded and fine
< HTTP/1.1 207 Multi-Status
< Server: nginx
< Date: Mon, 19 Feb 2024 09:23:56 GMT
< Content-Type: text/xml; charset=UTF-8
< Content-Length: 524
< Connection: keep-alive
< Keep-Alive: timeout=60
< Pragma: no-cache
< Cache-Control: no-cache

Response的正文:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://ns.jianguoyun.com">
<d:response>
<d:href>/dav/box1/Upload_test.txt</d:href>
<d:propstat>
<d:prop>
<m:Win32LastModifiedTime xmlns:m="urn:schemas-microsoft-com:"/>
<m:Win32FileAttributes xmlns:m="urn:schemas-microsoft-com:"/>
<m:Win32CreationTime xmlns:m="urn:schemas-microsoft-com:"/>
<m:Win32LastAccessTime xmlns:m="urn:schemas-microsoft-com:"/>
</d:prop><d:status>HTTP/1.1 200 OK</d:status>
</d:propstat></d:response></d:multistatus>

WebDav缓存

在上面的PROPFIND等请求的响应头中,能看到以下字段:

< Etag: 8sKBsnMc5tH71U67xjQTCQ
< Cache-Control: max-age=5

Cache-Control

Cache-Control: max-age=5 就是控制缓存的过期时间,这里是5秒后缓存过期。

Cache-Control也可以用来设置缓存类型,Cache-Control: private //私有缓存
Cache-Control: public //贡献缓存。

Etag

他们是用于HTTP缓存控制的字段。

Etag响应头,是HTTP中资源的特定版本标识符。
Etag相当于资源的指纹, URL 中的资源更改了,就一定要生成新的 ETag 值。
Etag由服务器生成,在客户端请求资源时通过Etag响应头发给客户端。

客户端下次请求同一个资源时,如果资源已经过期,客户端请求通过If-None-Match请求头,把Etag的值发给服务器,服务器可以通过If-None-Match的值,判断资源是否已经改变(这个过程叫做重新验证)。如果客户端的If-None-Match和服务器资源当前的Etag一致,服务器就不需要发送完整数据了,返回一个 304 Not Modified 状态即可。

测试

首先我用PROPFIND获取了WebDav中,一个文件的属性,它的Etag是:

<d:getetag>uLR1Dl0O-2f8uVxiMCSTGQ</d:getetag>

我在GET方法的请求头中,添加了If-None_Match字段,值就是刚才获取的文件Etag。

list = curl_slist_append(list, "If-None-Match: uLR1Dl0O-2f8uVxiMCSTGQ");

发送GET方法请求后,服务器返回304 Not Modified,即文件没有变动,无需重新获取数据。
在这里插入图片描述

强制重新验证

如果服务器想要客户端在资源没有过期的时候,也要获取最新的资源。
可以在 存在Etag或者Last-Modified头的同时,指定Cache-Control: no-cache或Cache-Control: max-age=0, must-revalidate

不需要缓存

指定Cache-Control: no-cache

GET、HEAD、OPTIONS 方法是幂等的,不会改变服务器资源的状态,是可以缓存的。
其余的方法是不建议缓存,或者不可以缓存的。

完整的缓存控制可以参考:【HTTP完全注解】看了还搞不懂缓存你直接来打我

WebDav的锁

WebDav 规范中存在排他锁、共享锁。
WebDav规范中只规定了写入锁(Write),不同的服务器可能实现了不同类型的锁。
在不同的服务器中,可能不支持锁,或者支持一种锁,或者支持多种锁。

WebDav中的每一个锁都会生成一个锁令牌(lock token),在对被锁住的对象进行操作室,HTTP头必须提交锁令牌信息。

有LOCK和UNLOCK方法来进行枷锁和解锁。

WebDav的状态码

WebDav扩展了以下状态码:
207:多状态,查看响应正文来获取详细状态。
422:请求URL存在,但是请求正文的XML内容不正确
423:请求的文件对象已被锁定
424:依赖失败,比如PROPPATCH中的一个属性修改命令失败,其余的命令也会失败。
507:服务器暂时无法提供存储空间

WebDav身份验证

通过TLS确保Basic验证信息安全。

WebDav版本控制

TODO.
参考:Versioning Extensions to WebDAV

WebDav和FTP的区别

WebDav提供了缓存功能,FTP没有;
WebDav一般通过HTTPS的443端口通信,FTP需要用20和21端口通信。
WebDav提供了锁,FTP没有。

参考

WebDAV Resources
WebDAV 规范文档
WebDAV 规范文档-Gitee
学习WebDav
如何在Zotero中设置webdav连接到坚果云?
【HTTP完全注解】看了还搞不懂缓存你直接来打我
http 三种认证方式 Basic Session Token 简介
Versioning Extensions to WebDAV

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/399601.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安卓adb调试备忘录

由于 MAC 的 USB 口全被占用着&#xff0c;采用无线连接刚方便&#xff0c;记录一下&#xff0c;以防忘记~ ADB原理 adb devices -l ## 列出连接的设备adb tcpip [端口号] adb tcpip 6666 # 将当前已连接USB上的Mobile端切换为TCP/IP模式&#xff0c;以6666端口进行监听. adb…

数据结构笔记1线性表及其实现

终于开始学习数据结构了 c语言结束之后 我们通过题目来巩固了 接下来我们来学习数据结构 这里我们将去认识到数据结构的一些基础知识&#xff0c;我在第一次学习的时候会很迷糊现在重新学习发现之前的时候还是因为c语言学的不牢固导致学习数据结构困难 这里 我会尽量的多写代码…

Day51 42 接雨水 84柱状图中的最大矩形

42 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1a;6解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,…

前端 webSocket 的使用

webSocket使用 注意要去监听websocket 对象事件&#xff0c;处理我们需要的数据 我是放在了最外层的index 内&#xff0c;监听编辑状态&#xff0c;去触发定义的方法。因为我这个项目是组件化开发&#xff0c;全部只有一个总编辑按钮&#xff0c;我只需监听是否触发了编辑即可…

深入探索STM32的存储选项:片内RAM、片内Flash与SDRAM

博客&#xff1a;深入探索STM32的存储选项&#xff1a;片内RAM、片内Flash与SDRAM 在嵌入式系统设计中&#xff0c;存储管理是一个至关重要的方面&#xff0c;尤其是对于基于STM32这类强大的微控制器来说。STM32系列微控制器因其高性能、低功耗以及灵活的存储选项而广受欢迎。本…

数智化转型的三大关键点

一、重新认识数智化转型 消费红利时代&#xff0c;伴随中国宏观经济向好发展&#xff0c;相当一部分企业可以轻松实现快速增长&#xff0c;如同搭乘了一架高速运转的电梯一路飞升。然而&#xff0c;随着宏观经济增速放缓&#xff0c;时代的电梯逐渐失去效力&#xff0c;中国商…

18. 四数之和 - 力扣(LeetCode)

问题描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; …

C#,洗牌问题(Card Shuffle Problem)的算法与源代码

1 洗牌问题&#xff08;Card Shuffle Problem&#xff09; 洗牌问题&#xff08;Card Shuffle Problem&#xff09;的基本描述 你有 100 张牌&#xff0c;从 1 到 100。 你把它们分成 k 堆&#xff0c;然后按顺序收集回来。 例如&#xff0c;如果您将它们分成 4 堆&#xff0…

关于发送邮件时Reply Reply All和Forward的区别

我们发送邮件的时候总是会纠结到底是用回复&#xff0c;还是回复全部&#xff0c;还是转发。 回复- 仅回复发件人。 全部回复- 回复发件人和抄送/密件抄送的联系人。 转发- 将电子邮件的副本发送给其他收件人。 这几种情形分别在什么时候用呢&#xff1f; 回复 比如Alen给你…

【设计模式】23种设计模式笔记

设计模式分类 模板方法模式 核心就是设计一个部分抽象类。 这个类具有少量具体的方法&#xff0c;和大量抽象的方法&#xff0c;具体的方法是为外界提供服务的点&#xff0c;具体方法中定义了抽象方法的执行序列 装饰器模式 现在有一个对象A&#xff0c;希望A的a方法被修饰 …

浏览器缓存机制

浏览器缓存有先后顺序&#xff0c;总体分为以下四个方面&#xff1a; Memory Cache Service Worker Cache Disk Cache Push Cache 缓存位置 1. Memory Cache 内存中的缓存 优点&#xff1a; 浏览器会优先去命中的一种缓存 响应速度最快 缺点&#xff1a; 缓存时间短&#xf…

ABAP 导入Excel表示例程序

目录 ABAP 导入excel示例程序创建程序使用的结构上传下载模板 ABAP 导入excel示例程序 批量导入程序&#xff0c;需要使用到导入模板&#xff0c;首先需要创建程序&#xff0c;之后是需要创建excel导入模板&#xff0c;并且需要将excel导入模板上传到SAP系统里面&#xff0c;之…

Code-Audit(代码审计)习题记录

介绍&#xff1a; 自己懒得搭建靶场了&#xff0c;靶场地址是 GitHub - CHYbeta/Code-Audit-Challenges: Code-Audit-Challenges为了方便在公网练习&#xff0c;可以随地访问&#xff0c;本文所有的题目均来源于网站HSCSEC-Code Audit 1、习题一 题目内容如下&#xff1a; 1…

运行jar时提示缺少依赖的类

供应商丢过来一个jar&#xff0c;是用Java写的Windows桌面程序&#xff0c;运行jar时提示缺少依赖的类&#xff0c;一看就是打包没带依赖的库&#xff0c;下面是解决方法&#xff1a; 1、解压缩jar&#xff0c;查看 META-INF 目录下的 MANIFEST.MF&#xff0c;看看都引用了哪些…

FlinkCDC详解

1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture&#xff08;数据变更捕获&#xff09;的简称。其核心原理就是监测并捕获数据库的变动&#xff08;例如增删改&#xff09;&#xff0c;将这些变更按照发生顺序捕获&#xff0c;将捕获到的数据&#xff0c;写入数据…

Cesium for Unreal 从源码编译到应用——插件编译

一、安装环境 Unreal Engine 5.3 CMake 3.17.5 Microsoft Visual Studio 2019 二、源码准备 下载cesium-unreal-samples工程。 git clone https://github.com/CesiumGS/cesium-unreal-samples.git 然后在工程目录创建Plugins文件夹&#xff0c;并下载cesium-unreal工程。 …

演练纪实│同创永益助力中交财务公司成功开展灾备应急演练

12月26日同创永益协助中交财务公司顺利完成核心业务系统、高性能云平台等关键业务系统的子系统验证演练&#xff0c;以及模拟切换演练、同城灾备应急演练。 同创永益北方交付二组的同事在演练前与中交财务公司演练负责人紧密沟通&#xff0c;展现了出色的专业素养和团队协作能力…

Vue3学习——标签的ref属性

在HTML标签上&#xff0c;可以使用相同的ref名称&#xff0c;得到DOM元素ref放在组件上时&#xff0c;拿到的是组件实例&#xff08;组件defineExpose暴露谁&#xff0c;ref才可以看到谁&#xff09; <script setup lang"ts"> import RefPractice from /compo…

测试C#调用Emgucv读取并显示视频文件

微信公众号“CSharp编程大全”的文章《C# 视频播放》介绍了基于emgucv读取视频文件并播放的用法&#xff08;百度文章名没有找到对象的文章地址&#xff0c;有兴趣的可以在微信中搜索该公众号并查看文章具体内容&#xff09;&#xff0c;本文学习并测试Emgucv播放视频文件的基本…

拼多多关键字搜索API-通过关键字获取拼多多商品列表商品价格商品id商品链接

拼多多根据关键词取商品列表 API 返回值说明 item_search-根据关键词取商品列表 公共参数 请求地址: pinduoduo/item_search 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&…