Skip to content

Extending status code key field in a response by a reason code #1482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
andy-maier opened this issue Feb 9, 2018 · 4 comments
Open

Extending status code key field in a response by a reason code #1482

andy-maier opened this issue Feb 9, 2018 · 4 comments
Labels
error desc Describing error responses beyond error codes and response schemas

Comments

@andy-maier
Copy link

andy-maier commented Feb 9, 2018

In a number of APIs that we have in the company, error responses for HTTP status 4XX and 5XX contain a message and a reason code. A particular HTTP status, say 400, usually has multiple reason codes. The reason codes are not globally unique, but the combination of an HTTP status code and reason code is unique and corresponds to a particular message (or rather message template, because there can be variables in it). Let's ignore the messages for a moment, but talk about the reason code.

My goal is to describe for each HTTP interaction (i.e. path & method) what the possible combinations of status code & reason code are that can happen for that specific interaction, and to have a description field for each such combination.

Second, I'd like the definitions of the complete set of defined combinations of status code and reason code along with its descriptions to be global, so that the definition of each response would only reference items from that global definition. There is a related issue #567.

Neither OpenAPI 2.0 nor 3.0 allows for the possibility to specify a combination of status code and reason code for a response. In the following excerpt of an OpenAPI 2.0 yaml file, I am using /definitions to define the format for error responses, and /responses to define the combinations of status code and reason code globally (messages / descriptions are left out for now), e.g. the response with key "error-400.1" stands for the combination of HTTP status code 400 and reason code 1. In the responses dictionary of an interaction (path+method), I want to reference the global /responses items that apply to this particular interaction:

{
    "definitions": {
        "error_response": {
            "type": "object"
            "properties": {
                "http-status": {
                    "format": "int32",
                    "type": "integer"
                },
                "reason": {
                    "format": "int32",
                    "type": "integer"
                },
            },
            "required": [
                "http-status",
                "reason",
            ],
        },
    },
    "responses": {
        "error-400.1": {
            "schema": {
                "$ref": "#/definitions/error_response"
            }
            "examples": {
                "application/json": {
                    "http-status": 400,
                    "reason": 1
                }
            },
        },
    },
    "paths": {
        "/api-tokens": {
            "post": {
                "summary": "Create an API token",
                "responses": {
                    "400.1": {                    # mark 1
                        "$ref": "#/responses/error-400.1"
                    }
                },
            }
        }
    },
}

The "mark 1" line is reported by the Swagger editor as an issue because the format of the key "400.1" is invalid. Reading the OpenAPI 3.0 spec, I find that the editor's reported issue is consistent with the spec, which defines that key as the HTTP status code or "default".

I think it is quite limiting to assign a fixed meaning to a dictionary key. In many other places, OpenAPI dictionary keys can be freely chosen and referenced. If that was possible with this key, the key could be a combination of status code and reason code, as I intended to do.

The extensibility mechanism of OpenAPI also does not help here I'm afraid, because all it can do is add extension fields to an already existing object. But in this case, the problem is that the meaning of the key (along with its checking in the editor) forces me to have only one object where I'd like to have multiple objects.

I think addressing this need is a hard problem given the format is as it is. For the record and for future consideration, I'd like to point out how limiting it was to assign a meaning to the key in this way, instead of allowing user-defined strings for the key and moving the status code into the object.

One possibility I'd like to bring up for discussion might be to simply allow user-defined strings starting with "x-" as keys (in lines such as "mark 1"), in addition to the already allowed status code values and "default". These user-defined strings could then be left alone by the editor or any tools that otherwise can continue to interpret a number as the HTTP status code.

@MikeRalphson
Copy link
Member

See also #1392

@andy-maier
Copy link
Author

After reading #1392, I'd like to point out two things:

  • I agree that OpenAPI should only describe at the HTTP level, and not go into application semantics.
  • I disagree with the advice to go to the HTTP standards group if one believes the HTTP status codes are insufficient. That would only make sense if the scope of the status codes was insufficient. I don't think that is the case. The problem is the missing detail, and the HTTP standards group cannot address that by adding more status codes.

Given these two statements above, the only solution I see is that application-specific details to existing HTTP status codes should be allowed. I can see exactly that in many APIs, i.e. people add API-specific details to 4XX and 5XX error responses. Some add numeric reason codes (like I mentioned above), some add tag-like strings (like @sekharbans mentions in #1392). That should all remain in the realm of the application, and does not need to be known by the OpenAPI spec.

However, I hope we agree that OpenAPI should allow that to be described.

If you can point out how I can describe which combination of status code and reason code can happen for a particular interaction, that would be helpful. I did not find a way with OpenAPI, so far.

@andy-maier
Copy link
Author

I did some investigation on my proposal to allow user-defined strings starting with "x-" as keys of the responses dictionary. To my surprise, it worked in the Swagger 2 and 3 editors on a OpenAPI 2.0 file, and is also permitted in both the OpenAPI 2.0 and 3.0 specs.

The reason I did not find it in the first place in the spec, was that I missed reading the innocent sentence "This object MAY be extended with Specification Extensions." at the end of the description of the "responsesObject" in the 3.0 spec (see here). The 2.0 spec states that more obviously as a row in the table. But it is there, and it was my fault to miss it. Sorry for that.

So I am making a small step of progress in what I want. The example from above now has become:

{
    "definitions": {
        "error_response": {
            "type": "object"
            "properties": {
                "http-status": {
                    "format": "int32",
                    "type": "integer"
                },
                "reason": {
                    "format": "int32",
                    "type": "integer"
                },
            },
            "required": [
                "http-status",
                "reason",
            ],
        },
    },
    "responses": {
        "error-400.1": {
            "description": "The request body could not be parsed as a valid JSON document."
            "schema": {
                "$ref": "#/definitions/error_response"
            }
            "examples": {
                "application/json": {
                    "http-status": 400,
                    "reason": 1
                }
            },
        },
    },
    "paths": {
        "/api-tokens": {
            "post": {
                "summary": "Create an API token",
                "responses": {
                    "x-400-1": {
                        "$ref": "#/responses/error-400.1"
                    }
                },
            }
        }
    },
}

My remaining issue with this is that the only place where the status code and reason code (and possibly other properties) show up now is in the example. I can live with that for now, but ultimately I think a more prescriptive approach is needed than examples.

@darrelmiller
Copy link
Member

darrelmiller commented Feb 14, 2018

If you are looking to describe the details of errors that are returned from an API then I would suggest you use the examples dictionary to do that. e.g.

openapi: 3.0.0
info:
  title: Test
  version: 1.0.0
paths:
  /invoices:
    post:
      responses:
        '200': 
          description: Successfully created the invoice
        '400':
          description: Failed to create the invoice due to client error
          content:
            'application/problem+json':
              examples:
                MissingAmount: 
                  value:
                    {
                      "type": "https://example.com/probs/invoice/missingamount",
                      "title": "Missing Amount",
                      "detail": "You are missing an invoice amount"
                    }
                MissingCustomer: 
                  value:
                    {
                      "type": "https://example.com/probs/invoice/missingcustomer",
                      "title": "Missing Customer",
                      "detail": "You are missing an customer code"
                    }

If you want a strict enumeration of the potential error scenarios then JSON Schema will allow you to do that.

OpenAPI's goal is to describe HTTP API semantics. Status codes are what HTTP APIs use to convey the result of a HTTP request. If there are additional application semantics related to failure conditions then they fall under the responsibility of the payload and their corresponding schemas. This is the same reasoning why OpenAPI does not have explicit semantics for paging, counts, filtering, even though they are common API constructs. There is no standard for these and therefore trying to use OpenAPI as a vehicle to create a standard would be problematic for our users who take a different approach.

Microsoft's efforts in the past to attempt to create an additional level of detail in HTTP status codes demonstrated this was a bad idea. How useful are these to you in your application?

400.1 - Invalid Destination Header.
400.2 - Invalid Depth Header.
400.3 - Invalid If Header.
400.4 - Invalid Overwrite Header.
400.5 - Invalid Translate Header.
400.6 - Invalid Request Body.
400.7 - Invalid Content Length.
400.8 - Invalid Timeout.
400.9 - Invalid Lock Token.

We need to learn from our mistakes and not make them again.

@handrews handrews added the error desc Describing error responses beyond error codes and response schemas label Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error desc Describing error responses beyond error codes and response schemas
Projects
None yet
Development

No branches or pull requests

4 participants