Skip to content

Validate json content in multipart/form-data request body #436

@emaincourt

Description

@emaincourt

Hi,

I've been trying to add an OpenAPI schema validation using the oapi-codegen validator which actually relies on your openapi3filter package and can't actually figure out how to make the validation pass when sending application/json content as part of a multipart/form-data body. I'm pretty new to kin-openapi and the whole OAPI3 ecosystem so I might be doing something wrong here. Please find below a reproducible failing example of what I'm trying to achieve:

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/getkin/kin-openapi/openapi3filter"
	"github.com/getkin/kin-openapi/routers/gorillamux"
)

const testSchema = `
openapi: 3.0.0
info:
  title: 'Validator'
  version: 0.0.1
paths:
  /test:
    post:
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                categories:
                  type: array
                  items:
                    $ref: "#/components/schemas/Category"
      responses:
        '200':
          description: Created

components:
  schemas:
    Category:
      type: object
      properties:
        name:
          type: string
      required:
        - name
`

func main() {
	swagger, err := openapi3.NewLoader().LoadFromData([]byte(testSchema))
	if err != nil {
		panic(err)
	}
	if err := swagger.Validate(context.Background()); err != nil {
		panic(err)
	}

	router, err := gorillamux.NewRouter(swagger)
	if err != nil {
		panic(err)
	}

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"`, "categories"))
	h.Set("Content-Type", "application/json")
	fw, err := writer.CreatePart(h)
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(fw, strings.NewReader(`[{"name": "foo"}]`))
	if err != nil {
		panic(err)
	}

	fw, err = writer.CreateFormFile("file", "hello.txt")
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(fw, strings.NewReader("hello"))
	if err != nil {
		panic(err)
	}

	writer.Close()

	req, _ := http.NewRequest(http.MethodPost, "/test", bytes.NewReader(body.Bytes()))
	req.Header.Set("Content-Type", writer.FormDataContentType())

	route, pathParams, _ := router.FindRoute(req)

	reqBody := route.Operation.RequestBody.Value

	requestValidationInput := &openapi3filter.RequestValidationInput{
		Request:    req,
		PathParams: pathParams,
		Route:      route,
	}

	err = openapi3filter.ValidateRequestBody(context.TODO(), requestValidationInput, reqBody)
	if err == nil {
		fmt.Println("Valid")
	} else {
		fmt.Printf("NOT valid. %s\n", err)
	}
}

When running this snippet, validation fails. Also if I remove the categories field, the validation passes.

Does the specification and the test scenario look correct ?

Thanks in advance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions