Goa v3에서 go:embed 사용 시험 오류

23504 단어 GoAPIhttpgoatech

개요


Go에 embed가 추가돼 자산이 바이너리에 박힐 수 있을 때 생각했죠.
이렇게 하면 Goa가 생성한 OpenAPI 문서를 바이너리에 포함합니다!잠깐만요.
따라서 Goa 정적 문서를 바이너리에 삽입하려고 합니다.

Files DSL 같은 DSL 찾아보기.


Files DSL은 Goa에서 제공하는 파일을 서비스하는 데 사용되는 DSL입니다.
아래와 같이 사용한다.
Files("/assets/{*filepath}", "/www/data/assets")
그러면 지정한 단점에 이 파일을 제공할 수 있습니다.상기 예라면 파일 /www/data/assets/x/y/z 을 접근 /assets/x/y/z 의 응답으로 되돌릴 수 있습니다.
이 DSL을 embed를 제공할 수 있는 파일로 바꾸면 됩니다.
····하지만, embed는 embed가 적힌 폴더 위에 있는 폴더를 볼 수 없다는 제약이 있다.Goa가 생성한 파일은 기본적으로gen 디렉터리에서 만들어졌기 때문에 아래에embed를 기술하면 위의 파일을 볼 수 없습니다.프로젝트의 최고층 목록에 embed를 지정하면 gen에서도 참조할 수 있지만 디자인을 재생성할 때gen 아래에 버려져 다시 제작된다는 방침은 좀 부적절하다.gooa example로 만든 파일(서비스 방법의 초기 형태 등)이 프로젝트의 최고층 디렉터리에 놓여 있기 때문에 gooa example로 생성하는 것이 가장 좋다만약 이 정도까지 한다면 스스로 쓰는 것이 비교적 빠를 것이다. (아마도 장래에 누군가가 그런 DSL을 만들 것이다.)

파일 다운로드 방법 모방,embed 활용


embed로 삽입된 파일은 지정한 파일 폴더를 포함하는 하위 파일이기 때문에 프로젝트의 고위 서비스 방법을 기술한 파일로 지정하는 것이 좋습니다.따라서 파일 다운로드 단점의 제작 방법을 모방해 본다.다운로드한 파일의 샘플은 goa/examplesupload_download에 있습니다.
embed로 이것을 다시 쓰고 자산을 매립해 보세요.

파일 지정


생성된 Openapi를 입력하십시오.json이 제공하는 단점을 지정하면 다음과 같습니다.

설계


요점은 지정
	Method("openapidoc", func() {
		Result(func() {
			Attribute("length", Int64, "Length is the downloaded content length in bytes.", func() {
				Example(4 * 1024 * 1024)
			})
			Attribute("encoding", String, func() {
				Example("application/json")
			})
			Required("length", "encoding")
		})

		Error("invalid_file_path", ErrorResult, "Could not locate file for download")
		Error("internal_error", ErrorResult, "Fault while processing download.")

		HTTP(func() {
			GET("/openapi.json")           // エンドポイント
			SkipResponseBodyEncodeDecode() // ←これがミソ。エンコードするコードを生成しないようになります
			Response(func() {
				Header("length:Content-Length") // ← ヘッダに Content-Length 指定できるようにする
				Header("encoding:Content-Type") // ← サービスメソッドで Content-Type を指定できるように
			})
			Response("invalid_file_path", StatusNotFound)          // ファイルないときのエラー
			Response("internal_error", StatusInternalServerError)  // その他エラー
		})
	})
gen이다.이것은 인코딩 응답을 생성하는 코드를 억제할 수 있다.SkipResponseBodyEncodeDecode()에 포함된 파일은 기술할 파일이 있는 폴더의 하위 파일이면 지정할 수 있습니다.폴더 위의 파일을 읽을 수 없습니다.포함된 파일을 지정하면 해당 위치에서 상대 경로로 표시되므로 파일을 지정할 때도 상대 경로를 지정해야 합니다.

서비스 방법


//go:embed gen/http/openapi.json   // ← 対象ファイルを embed.FS で埋め込み。複数ファイル埋め込めるがこの例ではひとつだけ
var openapijson embed.FS

// Openapidoc implements openapidoc.
func (s *assetsrvc) Openapidoc(ctx context.Context) (res *asset.OpenapidocResult, resp io.ReadCloser, err error) {
	f, err := openapijson.Open("gen/http/openapi.json") // ← 埋め込んだファイルを取り出し
	if err != nil {
		return nil, nil, asset.MakeInvalidFilePath(err)
	}
	fi, err := f.Stat() // ← 埋め込んだファイルのサイズを取得するための情報を得る
	if err != nil {
		return nil, nil, asset.MakeInternalError(err)
	}
	return &asset.OpenapidocResult{
		Length: fi.Size(), // ← ファイルサイズを指定
		Encoding: "application/json", // ← MIMEタイプを指定
	}, f, nil // ← ファイルは io.ReadCloser インターフェースを満たすのでそのまま返す
}
embed.FS에 여러 파일을 삽입할 수 있지만 이 예에는 한 파일만 삽입됩니다.포함된 파일의 상대 경로를 지정하여 포함된 파일을 추출할 수 있습니다.응답에서 이 파일을 되돌려 주는 동시에 파일 크기와 MIME 형식도 되돌려 주어야 한다. (← 파일 크기와 MIME 형식은 디자인에서DSL로 지정되어 있다.)빼기embed.FS를 통해 얻은 파일 크기Header를 알 수 있습니다.MIME 형식은 File#Stat() 이기 때문에 FileInfo 에서 이것을 지정합니다.

디렉토리 지정


다음 디렉터리 구조에서 application/json 이하는 Encoding 단점에서 공개됩니다.
.
├── asset
│   └── swaggerui
│       └── dist
│           ├── favicon-16x16.png
│           ├── favicon-32x32.png
│           ├── index.html
│           ├── oauth2-redirect.html
│           ├── swagger-ui-bundle.js
│           ├── swagger-ui-bundle.js.map
│           ├── swagger-ui-standalone-preset.js
│           ├── swagger-ui-standalone-preset.js.map
│           ├── swagger-ui.css
│           ├── swagger-ui.css.map
│           ├── swagger-ui.js
│           └── swagger-ui.js.map

설계


	Method("asset", func() {
		Payload(String, func() {                     // これはエンドポイントの {*filepath} に対応
			Description("Path to downloaded file.")
		})

		Result(func() {
			Attribute("length", Int64, "Length is the downloaded content length in bytes.", func() {
				Example(4 * 1024 * 1024)
			})
			Attribute("encoding", String, func() {
				Example("application/json")
			})
			Required("length", "encoding")
		})

		Error("invalid_file_path", ErrorResult, "Could not locate file for download")
		Error("internal_error", ErrorResult, "Fault while processing download.")

		HTTP(func() {
			GET("/asset/{*filename}")        // asset 以下に任意のファイルパスが書けるように {*filepath} で指定
			SkipResponseBodyEncodeDecode()   // ← レスポンスをエンコードするコードを生成しないようになります
			Response(func() {
				Header("length:Content-Length")  // ← ヘッダに Content-Length 指定できるようにする
				Header("encoding:Content-Type")  // ← サービスメソッドで Content-Type を指定できるように				
			})
			Response("invalid_file_path", StatusNotFound)         // ファイルないときのエラー
			Response("internal_error", StatusInternalServerError) // その他エラー
		})
	})

서비스 방법


//go:embed asset     // ← ディレクトリを指定することで配下が全て対象となります
var assets embed.FS

// Asset implements asset.
func (s *assetsrvc) Asset(ctx context.Context, p string) (res *asset.AssetResult, resp io.ReadCloser, err error) {
	f, err := assets.Open(filepath.Join("asset", p)) // ← p には asset 以下のパスが来るので、asset と連結して取り出す
	if err != nil {
		return nil, nil, asset.MakeInvalidFilePath(err)
	}
	fi, err := f.Stat()
	if err != nil {
		return nil, nil, asset.MakeInternalError(err)
	}
	return &asset.AssetResult{
		Length: fi.Size(),
		Encoding: mime.TypeByExtension(filepath.Ext(p)), // MIME タイプはファイル拡張子から類推
	}, f, nil
}
디렉터리를 지정하는 경우도 대체로 같다.단, 디렉터리 아래의 어떤 파일에 접근할지 모르기 때문에, MIME 형식은 접근 파일의 확장자 클래스에서 설정됩니다.

총결산


자산을 2진법에 포함시키면 대응하는 2진법의 OpenAPI 문서를 삽입할 수 있으며, 실제 문서와 관련된 분쟁을 없애거나 자산을 삽입하여 발표할 때의 분배를 간단하게 할 수 있다.나는 머지않아 누군가가 나에게 asset 같은 DSL과 플러그인을 만들어 줄 것이라고 생각한다.
아직 시용할 곳이 있지만 편리하게 사용할 수 있다.꼭 해보세요.و ̑̑
Happy hacking!

좋은 웹페이지 즐겨찾기