如何为 404 页面创建 OpenAPI 部分?

How do I create the OpenAPI section for the 404 page?

我正在使用 OpenApi 3. A tool I use, Owasp Zap 查看 OpenAPI 文档并创建虚假请求。当它收到 404 时,它会抱怨它没有 OpenAPI 承诺的媒体类型。

但是我没有在 OpenAPI 文档中写任何关于如何处理 404 的内容。显然我不能写出无限多的坏端点并记录它们 return 404s.

在 OpenAPI yaml 或 json 中记录此内容的正确方法是什么?

这是一个最小的 yaml 文件...我确信这个文件确实说明了 404,即。 404 不在合同中,因此工具抱怨 404 是有效响应,但 404 是站点在资源丢失时应该 return

---
"openapi": "3.0.0"

paths:
    /Foo/:
        get:
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: "#/components/schemas/Foo"
                default:
                    description: Errors
                    content:
                        application/json:
                            schema:
                                $ref: "#/components/schemas/Error"
components:
    schemas:
        Foo:
            type: object
            required:
                - name
            properties:
                name:
                    type: string
        Error:
            type: object
            required:
                - error
            properties:
                error:
                    type: string
                message:
                    type: string
                data:
                    type: object

已提出但未实施:https://github.com/OAI/OpenAPI-Specification/issues/521

在评论中有人给出了一个建议:https://github.com/OAI/OpenAPI-Specification/issues/521#issuecomment-513055351,这会减少你的代码,但你仍然需要为 N 个路径 * M 个方法插入 N*M 个条目。

既然我们没有能力根据自己的需要来改变规格,那我们就只能自己去适应了。

从您的个人资料来看,您似乎是 windows 用户。例如,您可以为 .yaml 文件 (Add menu item to windows context menu only for specific filetype, Adding a context menu item in Windows for a specific file extension) 创建一个新的资源管理器上下文菜单,并使其 运行 成为自动填充文件的脚本。

这里是一个名为 yamlfill404.py 的示例 python 脚本,它将以类似 path/to/pythonexecutable/python.exe path/to/python/script/yamlfill404.py %1 的方式在上下文调用中使用,其中 %1 是指向正在右键单击文件。

Python 文件:

import yaml
from sys import argv
import re

order = ['openapi','paths','components']
level0re = re.compile('(?<=\n)[^ ][^:]+')

def _propfill(rootnode, nodes, value):
    if len(nodes) == 1:
        rootnode[nodes[0]] = value
    if len(nodes) > 1:
        nextnode = rootnode.get(nodes[0]) 
        if rootnode.get(nodes[0]) is None:
            nextnode = {}
            rootnode[nodes[0]] = nextnode
        _propfill(nextnode, nodes[1:], value)

def propfill(rootnode, nodepath, value):
    _propfill(rootnode, [n.replace('__slash__','/') for n in nodepath.replace('\/','__slash__').split('/')], value)

def yamlfill(filepath):
    with open(filepath, 'r') as file:
        yamltree = yaml.safe_load(file)
    #propfill(yamltree, 'components/schemas/notFoundResponse/...', '')
    propfill(yamltree, 'components/responses/notFound/description', 'Not found response')
    propfill(yamltree, 'components/responses/notFound/content/application\/json/schema/$ref', '#/components/schemas/notFoundResponse')
    responses = [mv['responses'] if 'responses' in mv else [] for pk,pv in (yamltree['paths'].items() if 'paths' in yamltree else []) for mk,mv in pv.items()]
    for response in responses:
        propfill(response, '404/$ref', '#/components/responses/notFound')
    yamlstring = yaml.dump(yamltree)
    offsets = [i[1] for i in sorted([(order.index(f.group(0)) if f.group(0) in order else len(order),f.start()-1) for f in [f for f in level0re.finditer('\n'+yamlstring)]])]
    offsets = [(offset,(sorted([o for o in offsets if o > offset]+[len(yamlstring)-1])[0])) for offset in offsets]
    with open(filepath[:-5]+'_404.yaml', 'w') as file:
        file.write(''.join(['\n'+yamlstring[o[0]:o[1]] for o in offsets]).strip())

yamlfill(argv[-1])

它处理 %1,即 path/to/original.yaml 并将其保存为 path/to/original_404.yaml(但您可以更改它以覆盖原始文件)。

此示例脚本更改了 yaml 格式(引号类型、间距、顺序等),因为使用了库 pyyaml。我不得不用 order = ['openapi','paths','components'] 重新排序文件,因为它丢失了排序。为了减少干扰,也许更适合手动插入。也许一个只使用正则表达式的。 Maye使用awk,有很多方法。

不幸的是,这只是一个 hack 而不是解决方案。