From 1f6b334f1070845e4c4feb230bd1f24912b10fee Mon Sep 17 00:00:00 2001 From: marjune Date: Sat, 3 Aug 2024 19:37:38 +0800 Subject: [PATCH] feat(API): accept JSON request by header `Accept: application/json` BREAKING CHANGE Before: ```sh curl 'http://server/path?json' ``` After: ```sh curl -H 'Accept: application/json' 'http://server/path' ``` --- doc/en-US/api.md | 14 ++++++--- doc/zh-CN/api.md | 14 ++++++--- src/serverHandler/sessionData.go | 29 ++++++++++++----- test/case/042.index.bash | 20 ++++++------ test/lib.bash | 54 ++++++++++++++++---------------- test/main.bash | 2 +- 6 files changed, 77 insertions(+), 56 deletions(-) diff --git a/doc/en-US/api.md b/doc/en-US/api.md index b65587bd..6d36fffb 100644 --- a/doc/en-US/api.md +++ b/doc/en-US/api.md @@ -28,12 +28,13 @@ curl 'http://localhost/ghfs/?sort=/T' # Get JSON data of specified path ``` -GET ?json[&sort=key] +GET [?sort=key] +Accept: application/json ``` Example: ```sh -curl 'http://localhost/ghfs/?json' +curl -H 'Accept: application/json' 'http://localhost/ghfs/' ``` # Render page for downloading @@ -103,7 +104,8 @@ curl -X POST -d 'name=subdir1&name=subdir2/subdir21&name=file1&name=subdir3/file # Create directories in specific path Only work when "mkdir" is enabled. ``` -POST ?mkdir[&json] +POST ?mkdir +[Accept: application/json] name=&name=&...name= ``` @@ -116,7 +118,8 @@ curl -X POST -d 'name=dir1&name=dir2&name=foo/bar/baz' 'http://localhost/tmp/?mk # Upload files to specific path Only work when "upload" is enabled. ``` -POST ?upload[&json] +POST ?upload +[Accept: application/json] ``` - Must use `POST` method - Must use `multipart/form-data` encoding type @@ -145,7 +148,8 @@ curl -F 'innerdirfile=@file1.txt;filename=subdir/childdir/filename.txt' 'http:// Only work when "delete" is enabled. Directories will be deleted recursively. ``` -POST ?delete[&json] +POST ?delete +[Accept: application/json] name=&name=&...name= ``` diff --git a/doc/zh-CN/api.md b/doc/zh-CN/api.md index 05243ef8..6104e62f 100644 --- a/doc/zh-CN/api.md +++ b/doc/zh-CN/api.md @@ -28,12 +28,13 @@ curl 'http://localhost/ghfs/?sort=/T' # 获取指定路径JSON形式的数据 ``` -GET ?json[&sort=key] +GET [?sort=key] +Accept: application/json ``` 举例: ```sh -curl 'http://localhost/ghfs/?json' +curl -H 'Accept: application/json' 'http://localhost/ghfs/' ``` # 显示用于下载的页面 @@ -100,7 +101,8 @@ curl -X POST -d 'name=subdir1&name=subdir2/subdir21&name=file1&name=subdir3/file # 在指定路径下创建目录 仅在“mkdir”选项启用时有效。 ``` -POST ?mkdir[&json] +POST ?mkdir +[Accept: application/json] name=&name=&...name= ``` @@ -113,7 +115,8 @@ curl -X POST -d 'name=dir1&name=dir2&name=foo/bar/baz' 'http://localhost/tmp/?mk # 上传文件到指定路径 仅在“upload”选项启用时有效。 ``` -POST ?upload[&json] +POST ?upload +[Accept: application/json] ``` - 必须使用`POST`方法 - 必须使用`multipart/form-data`编码 @@ -142,7 +145,8 @@ curl -F 'innerdirfile=@file1.txt;filename=subdir/childdir/filename.txt' 'http:// 仅在“delete”选项启用时有效。 目录将被递归删除。 ``` -POST ?delete[&json] +POST ?delete +[Accept: application/json] name=&name=&...name= ``` diff --git a/src/serverHandler/sessionData.go b/src/serverHandler/sessionData.go index d68df22f..bb3e0efa 100644 --- a/src/serverHandler/sessionData.go +++ b/src/serverHandler/sessionData.go @@ -2,6 +2,7 @@ package serverHandler import ( "html/template" + "mjpclab.dev/ghfs/src/acceptHeaders" "mjpclab.dev/ghfs/src/i18n" "mjpclab.dev/ghfs/src/util" "net/http" @@ -11,6 +12,21 @@ import ( "strings" ) +const ( + noRedirect redirectAction = iota + addSlashSuffix + removeSlashSuffix +) + +const contentTypeJson = "application/json" + +var acceptContentTypes = []string{ + contentTypeJson, + "text/html", + "application/xhtml+xml", + "application/xml", +} + type pathEntry struct { Name string `json:"name"` Path string `json:"path"` @@ -27,12 +43,6 @@ type itemHtml struct { type redirectAction int -const ( - noRedirect redirectAction = iota - addSlashSuffix - removeSlashSuffix -) - type sessionContext struct { prefixReqPath string vhostReqPath string @@ -368,7 +378,10 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext, isDelete = true isMutate = true } - wantJson := strings.HasPrefix(rawQuery, "json") || strings.Contains(rawQuery, "&json") + + accepts := acceptHeaders.ParseAccepts(r.Header.Get("Accept")) + _, preferredContentType, _ := accepts.GetPreferredValue(acceptContentTypes) + wantJson := preferredContentType == contentTypeJson isRoot := vhostReqPath == "/" @@ -410,7 +423,7 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext, } restrictAccess, allowAccess := h.isAllowAccess(r, vhostReqPath, fsPath, file, item) - vary := "accept-encoding" + vary := "accept, accept-encoding" if restrictAccess { vary += ", referer, origin" } diff --git a/test/case/042.index.bash b/test/case/042.index.bash index 7764589f..0f90b7a8 100644 --- a/test/case/042.index.bash +++ b/test/case/042.index.bash @@ -7,40 +7,40 @@ sleep 0.05 # wait server ready # --index -cnt=$(curl_get_body 'http://127.0.0.1:3003/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://127.0.0.1:3003/go/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/go/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://127.0.0.1:3003/hello/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/hello/' | jq '.subItems | length') [ "$cnt" == "0" ] && fail "subItems should not be 0" # --index-dir -cnt=$(curl_get_body 'http://127.0.0.1:3003/world/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/world/' | jq '.subItems | length') [ "$cnt" == "0" ] && fail "subItems should not be 0" # --index-user -cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/x/y/z/a/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://baz:789@127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://baz:789@127.0.0.1:3003/x/y/z/a/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://foo:123@127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://foo:123@127.0.0.1:3003/x/y/z/a/' | jq '.subItems | length') [ "$cnt" == "0" ] && fail "subItems should not be 0" # --index-dir-user -cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://127.0.0.1:3003/x/y/z/b/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://baz:789@127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://baz:789@127.0.0.1:3003/x/y/z/b/' | jq '.subItems | length') assert "$cnt" '0' -cnt=$(curl_get_body 'http://bar:456@127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length') +cnt=$(curl_get_body -H 'accept: application/json' 'http://bar:456@127.0.0.1:3003/x/y/z/b/' | jq '.subItems | length') [ "$cnt" == "0" ] && fail "subItems should not be 0" jobs -p | xargs kill &> /dev/null diff --git a/test/lib.bash b/test/lib.bash index b9fc0e36..3994465b 100644 --- a/test/lib.bash +++ b/test/lib.bash @@ -1,52 +1,52 @@ #!/bin/bash assert() { - expect="$2" - actual="$1" + local expect="$2" + local actual="$1" if [ "$expect" != "$actual" ]; then echo -e "$(basename $0):${BASH_LINENO[0]} expect \"\e[0;32m$expect\e[0m\", got \"\e[1;31m$actual\e[0m\"" >&2 fi } fail() { - msg="$1" + local msg="$1" echo -e "$(basename $0):${BASH_LINENO[0]} \e[1;31m$msg\e[0m" >&2 } curl_head_status() { - args=($@) - urlindex=$[ ${#args[@]} - 1 ] - opts="${args[@]:0:urlindex}" - url="${args[urlindex]}" + local args=("$@") + local urlindex=$[ ${#args[@]} - 1 ] + local opts=("${args[@]:0:urlindex}") + local url="${args[urlindex]}" - curl -s -k -I $opts "$url" | head -n 1 | cut -d ' ' -f 2 + curl -s -k -I "${opts[@]}" "$url" | head -n 1 | cut -d ' ' -f 2 } curl_get_status() { - args=($@) - urlindex=$[ ${#args[@]} - 1 ] - opts="${args[@]:0:urlindex}" - url="${args[urlindex]}" + local args=("$@") + local urlindex=$[ ${#args[@]} - 1 ] + local opts=("${args[@]:0:urlindex}") + local url="${args[urlindex]}" - curl -s -k -i $opts "$url" | head -n 1 | cut -d ' ' -f 2 + curl -s -k -i "${opts[@]}" "$url" | head -n 1 | cut -d ' ' -f 2 } curl_get_header() { - args=($@) - urlindex=$[ ${#args[@]} - 1 ] - opts="${args[@]:0:urlindex}" - url="${args[urlindex]}" + local args=("$@") + local urlindex=$[ ${#args[@]} - 1 ] + local opts=("${args[@]:0:urlindex}") + local url="${args[urlindex]}" - curl -s -k -i $opts "$url" | sed -e '/^$/q' + curl -s -k -i "${opts[@]}" "$url" | sed -e '/^$/q' } curl_get_body() { - args=($@) - urlindex=$[ ${#args[@]} - 1 ] - opts="${args[@]:0:urlindex}" - url="${args[urlindex]}" + local args=("$@") + local urlindex=$[ ${#args[@]} - 1 ] + local opts=("${args[@]:0:urlindex}") + local url="${args[urlindex]}" - curl -s -k $opts "$url" + curl -s -k "${opts[@]}" "$url" } curl_post_status() { @@ -54,9 +54,9 @@ curl_post_status() { } curl_upload_content() { - url="$1" - name="$2" - value="$3" - filename="$4" + local url="$1" + local name="$2" + local value="$3" + local filename="$4" curl -s -k -F "$name=$value;filename=$filename" "$url" } diff --git a/test/main.bash b/test/main.bash index 8cfa7b70..94c0dead 100644 --- a/test/main.bash +++ b/test/main.bash @@ -2,7 +2,7 @@ cd $(dirname $0) -for cmd in realpath curl grep sed xargs; do +for cmd in realpath curl grep sed xargs jq; do type "$cmd" &> /dev/null if [ $? -ne 0 ]; then echo "command '$cmd' not found" >&2