Request with no content-type header are always validated

Issue #238 open
Christian Boitel created an issue

When validating a request with swagger-request-validator-core, a request with no content-type header gets validated even though spec includes valid content-types authorized for apiOperation.

Issue is located inside validateMediaTypes function inside com.atlassian.oai.validator.interaction.request.RequestValidator class:

Extract:

private ValidationReport validateMediaTypes(final Request request,
                                                final String headerName,
                                                final Collection<String> specMediaTypes,
                                                final String invalidTypeKey,
                                                final String notAllowedKey) {

        final Collection<String> requestHeaderValues = request.getHeaderValues(headerName);
        if (requestHeaderValues.isEmpty()) {
            return empty();
        }

As shown, function returns an empty validation report if Content-Type isn’t provided in request. It should do so only if specMediatTypes is an empty list.

Comments (9)

  1. Yusong Shen

    We encounter the same problem recently.

    https://swagger.io/docs/specification/media-types/ The OpenAPi spec doesn’t say anything when Content-Type header is missing.

    So I think this behaviour is in gray area. Do you suggest to validate body against the media-types listed in spec? In the case Content-Type header is missing, we have to first try to parse the body to determine its media type, it is hard to decide.

    I am leaning toward with current behaviour, if user want to make sure body is validated, they can explicitly required the content-type header, what do you think?

  2. Christian Boitel reporter

    There is only a grey area in the specification if you do not include in your api description file an expected content-type to be sent for an operation.

    But if you DO specify a content-type to be sent for an operation, then validator should ensure that such content-type has been sent for it has been described as required. Validator currently does not perform such check.

    Content-type request header shall be treated alike other specified parameters such as path/method/params checking:

    • if your api description states there is an operation for path with only method get and post, validator will correctly report a request with put isn’t supported.
    • if your api description states there is a parameter for an operation, validator will check if this parameter if being sent
    • if your api description states there is a required parameter for an operation, validator will check if it has been sent and matches the exepected schema definition provided if any

  3. James Navin

    Thanks for raising this. Just to confirm my understanding - you are expecting a validation error for a missing Content-Type header in a request if the spec defines a content type for the request body?

    The rationale behind not doing that is that many clients (incorrectly) either don’t set a content-type header or set the wrong one (e.g. send JSON with a text/plain header etc.). I was erring on the side of leniency. In this case the validator will validate as much as it can, but will ignore the request body validation.

    Perhaps a solution here would be to emit a validation message at WARN level by default, and then you can elect to increase that to an ERROR level validation to suit your needs. This would allow you to do stricter validation without breaking any existing users who rely on the current lenient behavior.

  4. Yusong Shen

    Specification also allows multiple content types for a request body (https://swagger.io/docs/specification/describing-request-body/).

    For example :

    paths:
      /pets:
        post:
          summary: Add a new pet
          requestBody:
            description: Optional description in *Markdown*
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Pet'
              application/xml:
                schema:
                  $ref: '#/components/schemas/Pet'
              application/x-www-form-urlencoded:
                schema:
                  $ref: '#/components/schemas/PetForm'
              text/plain:
                schema:
                  type: string
    

    Then in the case of absent content-type header, what should be the correct behaviour for above spec?

    In above case, the content of body can always be interpreted as plain string.

  5. Christian Boitel reporter

    In the petstore example above, it is stated that one of the content types provided (application/json, application/xml, application: x-www-form-urlencoded, text-plain) for a POST on /pets is expected, no more no less. Then if you send no content-type header or an unlisted content-type, it means your request isn’t valid.

    Now, let’s modify example provided by removing content definition under requestBody:

    paths:
      /pets:
        post:
          summary: Add a new pet
          requestBody:
            description: Optional description in *Markdown*
            required: true
    

    In such case, it is stated you can post on /pets, that a request body is required but no expectations is made about content-type. If someone sends a request with a missing or any content-type header, validation should succeed for API doesn’t state anything about it.

    As per using a warning, i believe:

    1. a validator should either report ok or ko, not maybe.
    2. If you do not want to control content-types, you may as well not include it in the spec you load.

    This is more of a philosophical point of view than anything else. I also understand changes made shall not break the contract set with previous versions. Reporting a warning you didn’t report previously might break any code using the library. So, i would instead:

    • Release a minor version of the library:

      • it would add a “enableStrongContentTypeValidation”

        • not calling it would keep the initial behaviour (silently ignore empty content-types)
        • calling it would make sure the validator strongly enforces content types provider in the given API spec either reporting a warning or error
      • i would also add a disableStrongxxx for future changes (see below)

      • Updated documentation would warn users of behaviour and invite them to:

        • explicitly enable for stronger validation
        • explicitly disable stronger validation to make users code resilient to future changes
      • Example:
        .createFor("http://api.myservice.com/swagger.json").enableStrongContentTypeValidation(WARNING)

    • At next major version release, i would make strong content-type behaviour a standard feature and warn in the documentation about the change.

      • those who explicitly called enable/disable will not be impacted
      • for others, major release change should be enough to warn them to look library changes in depth

    Hope it helps.

  6. Max Ludwig

    By the way, I just noticed that this is also the case with responses, as in, if the application returns a response with a status code that’s in the definition (like 200) but doesn’t have a content-type header and you validate that response it'll just pass.

  7. Full Name

    Bump, can this become an option as OP is suggesting?
    Sure, clients often missess the right content type but specs are there for a reason and needs to be followed, a null Content-Type where it’s requested it’s an error, the Actors needs to know ho to interpret the content, responses\request can have multiple content-type ecc..

    Right know we are fixing with a custom validator which checks for the Content-Type and then it delegates the validation to the Base Validator:

    Content contentSchema = requestBodySchema.getContent();
    boolean isContentRequired = requestBodySchema.getRequired() == null ? false : requestBodySchema.getRequired();
    
    if (isContentRequired && contentSchema != null && !contentSchema.isEmpty()) {   
        if (request.getContentType().isEmpty()) {
            return ValidationReport.singleton(
                this.normalValidatorMessages.create(
                "validation.request.contentType.notAllowed",
                "Required Content-Type is missing"
            ));
        }
    }
    

  8. Log in to comment