From 9ccb235a2d58d6b7caf406952f18ca13d9889f3e Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Fri, 10 Jul 2009 00:59:36 +0000 Subject: Apply patch from Benoit Chesneau's COUCHDB-404 Restores 0.8-style /db/_view view urls and adds an option to render views and documents as other formats like: /db/docid?show=blog/post /db/_view/blog/posts?list=index We're retaining the longer _design/appname paths as well because that resource is valuable for reverse proxies and rewriters. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@792771 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_httpd_db.erl | 89 +++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 36 deletions(-) (limited to 'src/couchdb/couch_httpd_db.erl') diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index f5bebc20..fcbdc4c6 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -26,7 +26,8 @@ -record(doc_query_args, { options = [], rev = nil, - open_revs = [] + open_revs = [], + show = nil }). % Database request handlers @@ -192,7 +193,6 @@ handle_design_info_req(#httpd{ handle_design_info_req(Req, _Db) -> send_method_not_allowed(Req, "GET"). - create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) -> ok = couch_httpd:verify_is_server_admin(Req), case couch_server:create(DbName, [{user_ctx, UserCtx}]) of @@ -558,8 +558,6 @@ all_docs_view(Req, Db, Keys) -> end end). - - db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) -> % check for the existence of the doc to handle the 404 case. couch_doc_open(Db, DocId, nil, []), @@ -572,43 +570,50 @@ db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) -> db_doc_req(#httpd{method='GET'}=Req, Db, DocId) -> #doc_query_args{ + show = Format, rev = Rev, open_revs = Revs, options = Options } = parse_doc_query(Req), - case Revs of - [] -> - Doc = couch_doc_open(Db, DocId, Rev, Options), - DiskEtag = couch_httpd:doc_etag(Doc), - couch_httpd:etag_respond(Req, DiskEtag, fun() -> - Headers = case Doc#doc.meta of - [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta - _ -> [] - end, - send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options)) - end); - _ -> - {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options), - {ok, Resp} = start_json_response(Req, 200), - send_chunk(Resp, "["), - % We loop through the docs. The first time through the separator - % is whitespace, then a comma on subsequent iterations. - lists:foldl( - fun(Result, AccSeparator) -> - case Result of - {ok, Doc} -> - JsonDoc = couch_doc:to_json_obj(Doc, Options), - Json = ?JSON_ENCODE({[{ok, JsonDoc}]}), - send_chunk(Resp, AccSeparator ++ Json); - {{not_found, missing}, RevId} -> - Json = ?JSON_ENCODE({[{"missing", RevId}]}), - send_chunk(Resp, AccSeparator ++ Json) + case Format of + nil -> + case Revs of + [] -> + Doc = couch_doc_open(Db, DocId, Rev, Options), + DiskEtag = couch_httpd:doc_etag(Doc), + couch_httpd:etag_respond(Req, DiskEtag, fun() -> + Headers = case Doc#doc.meta of + [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta + _ -> [] end, - "," % AccSeparator now has a comma - end, - "", Results), - send_chunk(Resp, "]"), - end_json_response(Resp) + send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options)) + end); + _ -> + {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options), + {ok, Resp} = start_json_response(Req, 200), + send_chunk(Resp, "["), + % We loop through the docs. The first time through the separator + % is whitespace, then a comma on subsequent iterations. + lists:foldl( + fun(Result, AccSeparator) -> + case Result of + {ok, Doc} -> + JsonDoc = couch_doc:to_json_obj(Doc, Options), + Json = ?JSON_ENCODE({[{ok, JsonDoc}]}), + send_chunk(Resp, AccSeparator ++ Json); + {{not_found, missing}, RevId} -> + Json = ?JSON_ENCODE({[{"missing", RevId}]}), + send_chunk(Resp, AccSeparator ++ Json) + end, + "," % AccSeparator now has a comma + end, + "", Results), + send_chunk(Resp, "]"), + end_json_response(Resp) + end; + _ -> + {DesignName, ShowName} = Format, + couch_httpd_show:handle_doc_show(Req, DesignName, ShowName, DocId, Db) end; db_doc_req(#httpd{method='POST'}=Req, Db, DocId) -> @@ -843,6 +848,16 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts) db_attachment_req(Req, _Db, _DocId, _FileNameParts) -> send_method_not_allowed(Req, "DELETE,GET,HEAD,PUT"). +parse_doc_format(FormatStr) when is_binary(FormatStr) -> + parse_doc_format(?b2l(FormatStr)); +parse_doc_format(FormatStr) when is_list(FormatStr) -> + SplitFormat = lists:splitwith(fun($/) -> false; (_) -> true end, FormatStr), + case SplitFormat of + {DesignName, [$/ | ShowName]} -> {?l2b(DesignName), ?l2b(ShowName)}; + _Else -> throw({bad_request, <<"Invalid doc format">>}) + end; +parse_doc_format(_BadFormatStr) -> + throw({bad_request, <<"Invalid doc format">>}). parse_doc_query(Req) -> lists:foldl(fun({Key,Value}, Args) -> @@ -875,6 +890,8 @@ parse_doc_query(Req) -> {"open_revs", RevsJsonStr} -> JsonArray = ?JSON_DECODE(RevsJsonStr), Args#doc_query_args{open_revs=[couch_doc:parse_rev(Rev) || Rev <- JsonArray]}; + {"show", FormatStr} -> + Args#doc_query_args{show=parse_doc_format(FormatStr)}; _Else -> % unknown key value pair, ignore. Args end -- cgit v1.2.3