11 Star 11 Fork 0

Gitee 极速下载 / goa

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/goadesign/goa
克隆/下载
http.go 37.54 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
package dsl
import (
"fmt"
"strconv"
"strings"
"goa.design/goa/eval"
"goa.design/goa/expr"
)
const (
StatusContinue = expr.StatusContinue
StatusSwitchingProtocols = expr.StatusSwitchingProtocols
StatusProcessing = expr.StatusProcessing
StatusOK = expr.StatusOK
StatusCreated = expr.StatusCreated
StatusAccepted = expr.StatusAccepted
StatusNonAuthoritativeInfo = expr.StatusNonAuthoritativeInfo
StatusNoContent = expr.StatusNoContent
StatusResetContent = expr.StatusResetContent
StatusPartialContent = expr.StatusPartialContent
StatusMultiStatus = expr.StatusMultiStatus
StatusAlreadyReported = expr.StatusAlreadyReported
StatusIMUsed = expr.StatusIMUsed
StatusMultipleChoices = expr.StatusMultipleChoices
StatusMovedPermanently = expr.StatusMovedPermanently
StatusFound = expr.StatusFound
StatusSeeOther = expr.StatusSeeOther
StatusNotModified = expr.StatusNotModified
StatusUseProxy = expr.StatusUseProxy
StatusTemporaryRedirect = expr.StatusTemporaryRedirect
StatusPermanentRedirect = expr.StatusPermanentRedirect
StatusBadRequest = expr.StatusBadRequest
StatusUnauthorized = expr.StatusUnauthorized
StatusPaymentRequired = expr.StatusPaymentRequired
StatusForbidden = expr.StatusForbidden
StatusNotFound = expr.StatusNotFound
StatusMethodNotAllowed = expr.StatusMethodNotAllowed
StatusNotAcceptable = expr.StatusNotAcceptable
StatusProxyAuthRequired = expr.StatusProxyAuthRequired
StatusRequestTimeout = expr.StatusRequestTimeout
StatusConflict = expr.StatusConflict
StatusGone = expr.StatusGone
StatusLengthRequired = expr.StatusLengthRequired
StatusPreconditionFailed = expr.StatusPreconditionFailed
StatusRequestEntityTooLarge = expr.StatusRequestEntityTooLarge
StatusRequestURITooLong = expr.StatusRequestURITooLong
StatusUnsupportedMediaType = expr.StatusUnsupportedMediaType
StatusRequestedRangeNotSatisfiable = expr.StatusRequestedRangeNotSatisfiable
StatusExpectationFailed = expr.StatusExpectationFailed
StatusTeapot = expr.StatusTeapot
StatusUnprocessableEntity = expr.StatusUnprocessableEntity
StatusLocked = expr.StatusLocked
StatusFailedDependency = expr.StatusFailedDependency
StatusUpgradeRequired = expr.StatusUpgradeRequired
StatusPreconditionRequired = expr.StatusPreconditionRequired
StatusTooManyRequests = expr.StatusTooManyRequests
StatusRequestHeaderFieldsTooLarge = expr.StatusRequestHeaderFieldsTooLarge
StatusUnavailableForLegalReasons = expr.StatusUnavailableForLegalReasons
StatusInternalServerError = expr.StatusInternalServerError
StatusNotImplemented = expr.StatusNotImplemented
StatusBadGateway = expr.StatusBadGateway
StatusServiceUnavailable = expr.StatusServiceUnavailable
StatusGatewayTimeout = expr.StatusGatewayTimeout
StatusHTTPVersionNotSupported = expr.StatusHTTPVersionNotSupported
StatusVariantAlsoNegotiates = expr.StatusVariantAlsoNegotiates
StatusInsufficientStorage = expr.StatusInsufficientStorage
StatusLoopDetected = expr.StatusLoopDetected
StatusNotExtended = expr.StatusNotExtended
StatusNetworkAuthenticationRequired = expr.StatusNetworkAuthenticationRequired
)
// HTTP defines the HTTP transport specific properties of an API, a service or a
// single method. The function maps the method payload and result types to HTTP
// properties such as parameters (via path wildcards or query strings), request
// or response headers, request or response bodies as well as response status
// code. HTTP also defines HTTP specific properties such as the method endpoint
// URLs and HTTP methods.
//
// The functions that appear in HTTP such as Header, Param or Body may take
// advantage of the method payload or result types (depending on whether they
// appear when describing the HTTP request or response). The properties of the
// header, parameter or body attributes inherit the properties of the attributes
// with the same names that appear in the method payload or result types.
//
// HTTP must appear in an API, a Service, or a Method expression.
//
// HTTP accepts an optional argument which is the defining DSL function.
//
// Example:
//
// var _ = API("calc", func() {
// HTTP(func() {
// Path("/api") // Prefix to HTTP path of all requests.
// })
// })
//
// Example:
//
// var _ = Service("calculator", func() {
// Error("unauthorized")
//
// HTTP(func() {
// Path("/calc") // Prefix to all request paths
// Error("unauthorized", StatusUnauthorized) // Define "unauthorized"
// // error HTTP response status code.
// Parent("account") // Parent service, used to prefix request
// // paths.
// CanonicalMethod("show") // Method whose path is used to prefix
// // the paths of child service.
// })
//
// Method("div", func() {
// Description("Divide two operands.")
// Payload(Operands)
// Error("div_by_zero")
//
// HTTP(func() {
// GET("/div/{left}/{right}") // Define HTTP route. The "left"
// // and "right" parameter properties
// // are inherited from the
// // corresponding Operands attributes.
// Param("integer:int") // Load "integer" attribute of
// // Operands from "int" query string.
// Header("requestID:X-RequestId") // Load "requestID" attribute
// // of Operands from
// // X-RequestId header
// Response(StatusOK) // Use status 200 on success
// Error("div_by_zero", BadRequest) // Use status code 400 for
// // "div_by_zero" responses
// })
// })
// })
//
func HTTP(fns ...func()) {
if len(fns) > 1 {
eval.InvalidArgError("zero or one function", fmt.Sprintf("%d functions", len(fns)))
return
}
fn := func() {}
if len(fns) == 1 {
fn = fns[0]
}
switch actual := eval.Current().(type) {
case *expr.APIExpr:
eval.Execute(fn, expr.Root)
case *expr.ServiceExpr:
res := expr.Root.API.HTTP.ServiceFor(actual)
res.DSLFunc = fn
case *expr.MethodExpr:
res := expr.Root.API.HTTP.ServiceFor(actual.Service)
act := res.EndpointFor(actual.Name, actual)
act.DSLFunc = fn
default:
eval.IncompatibleDSL()
}
}
// Consumes adds a MIME type to the list of MIME types the APIs supports when
// accepting requests. While the DSL supports any MIME type, the code generator
// only knows to generate the code for "application/json", "application/xml" and
// "application/gob". The service code must provide the decoders for other MIME
// types.
//
// Consumes must appear in the HTTP expression of API.
//
// Consumes accepts one or more strings corresponding to the MIME types.
//
// Example:
//
// API("cellar", func() {
// // ...
// HTTP(func() {
// Consumes("application/json", "application/xml")
// // ...
// })
// })
//
func Consumes(args ...string) {
switch e := eval.Current().(type) {
case *expr.RootExpr:
e.API.HTTP.Consumes = append(e.API.HTTP.Consumes, args...)
default:
eval.IncompatibleDSL()
}
}
// Produces adds a MIME type to the list of MIME types the APIs supports when
// writing responses. While the DSL supports any MIME type, the code generator
// only knows to generate the code for "application/json", "application/xml" and
// "application/gob". The service code must provide the encoders for other MIME
// types.
//
// Produces must appear in the HTTP expression of API.
//
// Produces accepts one or more strings corresponding to the MIME types.
//
// Example:
//
// API("cellar", func() {
// // ...
// HTTP(func() {
// Produces("application/json", "application/xml")
// // ...
// })
// })
//
func Produces(args ...string) {
switch e := eval.Current().(type) {
case *expr.RootExpr:
e.API.HTTP.Produces = append(e.API.HTTP.Produces, args...)
default:
eval.IncompatibleDSL()
}
}
// Path defines an API or service base path, i.e. a common HTTP path prefix to
// all the API or service methods. The path may define wildcards (see GET for a
// description of the wildcard syntax). The corresponding parameters must be
// described using Params. Multiple base paths may be defined for services.
//
// GET("/") does not add a trailing slash when the base path is defined by Path.
// For example, when Path('foo') is defined, the path generated by GET("/") will be '/foo'.
// As a special case, if you want to generate a path with a trailing slash, you can use
// GET("/./") to generate a path such as '/foo/'.
//
// Path must appear in a API HTTP expression or a Service HTTP expression.
//
// Path accepts one argument: the HTTP path prefix.
func Path(val string) {
switch def := eval.Current().(type) {
case *expr.RootExpr:
if expr.Root.API.HTTP.Path != "" {
eval.ReportError(`only one base path may be specified for an API, got base paths %q and %q`, expr.Root.API.HTTP.Path, val)
}
expr.Root.API.HTTP.Path = val
case *expr.HTTPServiceExpr:
if !strings.HasPrefix(val, "//") {
rp := expr.Root.API.HTTP.Path
awcs := expr.ExtractHTTPWildcards(rp)
wcs := expr.ExtractHTTPWildcards(val)
for _, awc := range awcs {
for _, wc := range wcs {
if awc == wc {
eval.ReportError(`duplicate wildcard "%s" in API and service base paths`, wc)
}
}
}
}
def.Paths = append(def.Paths, val)
default:
eval.IncompatibleDSL()
}
}
// GET defines a route using the GET HTTP method. The route may use wildcards to
// define path parameters. Wildcards start with '{' or with '{*' and end with
// '}'. They must appear after a '/'.
//
// A wildcard that starts with '{' matches a section of the path (the value in
// between two slashes).
//
// A wildcard that starts with '{*' matches the rest of the path. Such wildcards
// must terminate the path.
//
// GET must appear in a method HTTP function.
//
// GET accepts one argument which is the request path.
//
// Example:
//
// var _ = Service("Manager", func() {
// Method("GetAccount", func() {
// Payload(GetAccount)
// Result(Account)
// HTTP(func() {
// GET("/{accountID}/details")
// GET("/{*accountPath}")
// })
// })
// })
func GET(path string) *expr.RouteExpr {
return route("GET", path)
}
// HEAD creates a route using the HEAD HTTP method. See GET.
func HEAD(path string) *expr.RouteExpr {
return route("HEAD", path)
}
// POST creates a route using the POST HTTP method. See GET.
func POST(path string) *expr.RouteExpr {
return route("POST", path)
}
// PUT creates a route using the PUT HTTP method. See GET.
func PUT(path string) *expr.RouteExpr {
return route("PUT", path)
}
// DELETE creates a route using the DELETE HTTP method. See GET.
func DELETE(path string) *expr.RouteExpr {
return route("DELETE", path)
}
// OPTIONS creates a route using the OPTIONS HTTP method. See GET.
func OPTIONS(path string) *expr.RouteExpr {
return route("OPTIONS", path)
}
// TRACE creates a route using the TRACE HTTP method. See GET.
func TRACE(path string) *expr.RouteExpr {
return route("TRACE", path)
}
// CONNECT creates a route using the CONNECT HTTP method. See GET.
func CONNECT(path string) *expr.RouteExpr {
return route("CONNECT", path)
}
// PATCH creates a route using the PATCH HTTP method. See GET.
func PATCH(path string) *expr.RouteExpr {
return route("PATCH", path)
}
func route(method, path string) *expr.RouteExpr {
r := &expr.RouteExpr{Method: method, Path: path}
a, ok := eval.Current().(*expr.HTTPEndpointExpr)
if !ok {
eval.IncompatibleDSL()
return r
}
r.Endpoint = a
a.Routes = append(a.Routes, r)
return r
}
// Header describes a single HTTP header or gRPC metadata header. The properties
// (description, type, validation etc.) of a header are inherited from the
// request or response type attribute with the same name by default.
//
// Header must appear in the API HTTP expression (to define request headers
// common to all the API endpoints), a service HTTP expression (to define
// request headers common to all the service endpoints) a specific method HTTP
// expression (to define request headers) or a Response expression (to define
// the response headers). Header may also appear in a method GRPC expression (to
// define headers sent in message metadata), or in a Response expression (to
// define headers sent in result metadata). Finally Header may also appear in a
// Headers expression.
//
// Header accepts the same arguments as the Attribute function. The header name
// may define a mapping between the attribute name and the HTTP header name when
// they differ. The mapping syntax is "name of attribute:name of header".
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Payload(CreatePayload)
// Result(Account)
// HTTP(func() {
// Header("auth:Authorization", String, "Auth token", func() {
// Pattern("^Bearer [^ ]+$")
// })
// Response(StatusCreated, func() {
// Header("href") // Inherits description, type, validations
// // etc. from Account href attribute
// })
// })
// })
// })
//
func Header(name string, args ...interface{}) {
h := headers(eval.Current())
if h == nil {
eval.IncompatibleDSL()
return
}
if name == "" {
eval.ReportError("header name cannot be empty")
}
eval.Execute(func() { Attribute(name, args...) }, h.AttributeExpr)
h.Remap()
}
// Cookie identifies a HTTP cookie. When used within a Response the Cookie DSL
// also makes it possible to define the cookie attributes.
//
// Cookie must appear in the API HTTP expression (to define request cookies
// common to all the API endpoints), a service HTTP expression (to define
// request cookies common to all the service endpoints) a specific method HTTP
// expression (to define request cookies) or a Response expression (to define
// the response cookies).
//
// Cookie accepts the same arguments as the Attribute function. The cookie name
// may define a mapping between the attribute name and the cookie name. The
// mapping syntax is "name of attribute:name of cookie".
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Payload(func() {
// Attribute("session", String, "ID of current session")
// })
// Result(Account)
// HTTP(func() {
// // Initialize payload's "session" attribute with the value of
// // the SID cookie after validating that's it's a valid GUID.
// Cookie("session:SID", String, func() {
// Format(FormatGUID)
// })
// Response(StatusCreated, func() {
// // Write the value of the result "session" attribute to
// // the cookie named "SID" and initialize the cookie
// // "max-age", "domain", "path", "secure" and "http-only"
// // attributes. When reading the cookie value client
// // side validate that's it is a GUID.
// Cookie("session:SID", String, func() {
// Format(FormatGUID) // Cookie value validations
// })
// CookieMaxAge(3600) // Cookie attributes
// CookieDomain("goa.design")
// CookiePath("/session")
// CookieSecure()
// CookieHTTPOnly()
// })
// })
// })
// })
//
func Cookie(name string, args ...interface{}) {
h := cookies(eval.Current())
if h == nil {
eval.IncompatibleDSL()
return
}
if name == "" {
eval.ReportError("header name cannot be empty")
}
eval.Execute(func() { Attribute(name, args...) }, h.AttributeExpr)
h.Remap()
}
// CookieMaxAge defines the "max-age" attribute of a HTTP response cookie.
//
// CookieMaxAge must appear in a Cookie expression.
//
// CookieMaxAge accepts one argument which is the max-age value.
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Result(Account)
// HTTP(func() {
// Response(StatusCreated, func() {
// Cookie("session:SID", String)
// CookieMaxAge(3600)
// })
// })
// })
// })
//
func CookieMaxAge(n int) {
_, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
cookieAttribute("max-age", strconv.Itoa(n))
}
// CookieDomain defines the "domain" attribute of a HTTP response cookie.
//
// CookieDomain must appear in a Cookie expression.
//
// CookieDomain accepts one argument which is the path value.
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Result(Account)
// HTTP(func() {
// Response(StatusCreated, func() {
// Cookie("session:SID", String)
// CookieDomain("goa.design")
// })
// })
// })
// })
//
func CookieDomain(d string) {
_, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
cookieAttribute("domain", d)
}
// CookiePath defines the "path" attribute of a HTTP response cookie.
//
// CookiePath must appear in a Cookie expression.
//
// CookiePath accepts one argument which is the path value.
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Result(Account)
// HTTP(func() {
// Response(StatusCreated, func() {
// Cookie("session:SID", String)
// CookiePath("/session")
// })
// })
// })
// })
//
func CookiePath(p string) {
_, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
cookieAttribute("path", p)
}
// CookieSecure initializes the "secute" attribute of a HTTP response cookie
// with "Secure".
//
// CookieSecure must appear in a Cookie expression.
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Result(Account)
// HTTP(func() {
// Response(StatusCreated, func() {
// Cookie("session:SID", String)
// CookieSecure()
// })
// })
// })
// })
//
func CookieSecure() {
_, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
cookieAttribute("secure", "Secure")
}
// CookieHTTPOnly initializes the "http-only" attribute of a HTTP response
// cookie with "HttpOnly".
//
// CookieHTTPOnly must appear in a Cookie expression.
//
// Example:
//
// var _ = Service("account", func() {
// Method("create", func() {
// Result(Account)
// HTTP(func() {
// Response(StatusCreated, func() {
// Cookie("session:SID", String)
// CookieHTTPOnly()
// })
// })
// })
// })
//
func CookieHTTPOnly() {
_, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
cookieAttribute("http-only", "HttpOnly")
}
// Params groups a set of Param expressions. It makes it possible to list
// required parameters using the Required function.
//
// Params must appear in an API or Service HTTP expression to define the API or
// service base path and query string parameters. Params may also appear in an
// method HTTP expression to define the HTTP endpoint path and query string
// parameters.
//
// Params accepts one argument which is a function listing the parameters.
//
// Example:
//
// var _ = API("cellar", func() {
// HTTP(func() {
// Params(func() {
// Param("version", String, "API version", func() {
// Enum("1.0", "2.0")
// })
// Required("version")
// })
// })
// })
//
func Params(args interface{}) {
p := params(eval.Current())
if p == nil {
eval.IncompatibleDSL()
return
}
fn, ok := args.(func())
if !ok {
eval.InvalidArgError("function", args)
return
}
eval.Execute(fn, p)
}
// Param describes a single HTTP request path or query string parameter.
//
// Param must appear in the API HTTP expression (to define request parameters
// common to all the API endpoints), a service HTTP expression to define common
// parameters to all the service methods or a specific method HTTP
// expression. Param may also appear in a Params expression.
//
// Param accepts the same arguments as the Function Attribute.
//
// The name may be of the form "name of attribute:name of parameter" to define a
// mapping between the attribute and parameter names when they differ.
//
// Example:
//
// var ShowPayload = Type("ShowPayload", func() {
// Attribute("id", UInt64, "Account ID")
// Attribute("version", String, "Version", func() {
// Enum("1.0", "2.0")
// })
// })
//
// var _ = Service("account", func() {
// HTTP(func() {
// Path("/{parentID}")
// Param("parentID", UInt64, "ID of parent account")
// })
// Method("show", func() { // default response type.
// Payload(ShowPayload)
// Result(AccountResult)
// HTTP(func() {
// GET("/{id}") // HTTP request uses ShowPayload "id"
// // attribute to define "id" parameter.
// Params(func() { // Params makes it possible to group
// // Param expressions.
// Param("version:v") // "version" of ShowPayload to define
// // path and query string parameters.
// // Query string "v" maps to attribute
// // "version" of ShowPayload.
// })
// })
// })
// })
//
func Param(name string, args ...interface{}) {
p := params(eval.Current())
if p == nil {
eval.IncompatibleDSL()
return
}
if name == "" {
eval.ReportError("parameter name cannot be empty")
}
eval.Execute(func() { Attribute(name, args...) }, p.AttributeExpr)
p.Remap()
}
// MapParams describes the query string parameters in a HTTP request.
//
// MapParams must appear in a Method HTTP expression to map the query string
// parameters with the Method's Payload.
//
// MapParams accepts one optional argument which specifes the Payload
// attribute to which the query string parameters must be mapped. This Payload
// attribute must be a map. If no argument is specified, the query string
// parameters are mapped with the entire Payload (the Payload must be a map).
//
// Example:
//
// var _ = Service("account", func() {
// Method("index", func() {
// Payload(MapOf(String, Int))
// HTTP(func() {
// GET("/")
// MapParams()
// })
// })
// })
//
// var _ = Service("account", func() {
// Method("show", func() {
// Payload(func() {
// Attribute("p", MapOf(String, String))
// Attribute("id", String)
// })
// HTTP(func() {
// GET("/{id}")
// MapParams("p")
// })
// })
// })
//
func MapParams(args ...interface{}) {
if len(args) > 1 {
eval.ReportError("too many arguments")
}
e, ok := eval.Current().(*expr.HTTPEndpointExpr)
if !ok {
eval.IncompatibleDSL()
return
}
var mapName string
if len(args) > 0 {
mapName, ok = args[0].(string)
if !ok {
eval.ReportError("argument must be a string")
}
}
e.MapQueryParams = &mapName
}
// MultipartRequest indicates that HTTP requests made to the method use
// MIME multipart encoding as defined in RFC 2046.
//
// MultipartRequest must appear in a HTTP endpoint expression.
//
// goa generates a custom encoder that writes the payload for requests made to
// HTTP endpoints that use MultipartRequest. The generated encoder accept a
// user provided function that does the actual mapping of the payload to the
// multipart content. The user provided function accepts a multipart writer
// and a reference to the payload and is responsible for encoding the payload.
// goa also generates a custom decoder that reads back the multipart content
// into the payload struct. The generated decoder also accepts a user provided
// function that takes a multipart reader and a reference to the payload struct
// as parameter. The user provided decoder is responsible for decoding the
// multipart content into the payload. The example command generates a default
// implementation for the user decoder and encoder.
//
func MultipartRequest() {
e, ok := eval.Current().(*expr.HTTPEndpointExpr)
if !ok {
eval.IncompatibleDSL()
return
}
e.MultipartRequest = true
}
// SkipRequestBodyEncodeDecode prevents Goa from generating the request encoding
// (client) and decoding (server) code. Instead the service method gets direct
// access to the HTTP body reader. The client method provides a reader from
// which to stream the request body. This makes it possible to stream requests
// without requiring the entire content to be loaded in memory for
// encoding/decoding. Note that the use of this function is incompatible with
// gRPC and calling it on a method that defines a gRPC transport is an error.
//
// SkipRequestBodyEncodeDecode must appear in a HTTP endpoint expression.
//
// Example:
//
// var _ = Service("upload", func() {
// Method("upload", func() {
// Payload(func() {
// Attribute("id", String)
// Attribute("length", Int)
// })
// HTTP(func() {
// POST("/{id}")
// Header("length:Content-Length")
// SkipRequestBodyEncodeDecode()
// })
// })
//
func SkipRequestBodyEncodeDecode() {
e, ok := eval.Current().(*expr.HTTPEndpointExpr)
if !ok {
eval.IncompatibleDSL()
return
}
e.SkipRequestBodyEncodeDecode = true
}
// SkipResponseBodyEncodeDecode prevents Goa from generating the response
// encoding (server) and decoding (client) code. Instead the service method
// returns a reader from which to stream the HTTP response body io. The client
// also gets access to a reader to stream the incoming response body. This makes
// it possible to stream responses without requiring the entire content to be
// loaded in memory for encoding/decoding. Note that the use of this function is
// incompatible with gRPC and calling it on a method that defines a gRPC
// transport is an error.
//
// SkipResponseBodyEncodeDecode must appear in a HTTP endpoint expression.
//
// Example:
//
// var _ = Service("download", func() {
// Method("download", func() {
// Payload(String)
// Result(func() {
// Attribute("length", Int)
// })
// HTTP(func() {
// POST("/{id}")
// SkipResponseBodyEncodeDecode()
// Response(StatusOK, func() {
// Header("length:Content-Length")
// })
// })
// })
//
func SkipResponseBodyEncodeDecode() {
e, ok := eval.Current().(*expr.HTTPEndpointExpr)
if !ok {
eval.IncompatibleDSL()
return
}
e.SkipResponseBodyEncodeDecode = true
}
// Body describes a HTTP request or response body.
//
// Body must appear in a Method HTTP expression to define the request body or in
// an Error or Result HTTP expression to define the response body. If Body is
// absent then the body is built using the HTTP endpoint request or response
// type attributes not used to describe parameters (request only) or headers.
//
// Body accepts one argument which describes the shape of the body, it can be:
//
// - The name of an attribute of the request or response type. In this case the
// attribute type describes the shape of the body.
//
// - A function listing the body attributes. The attributes inherit the
// properties (description, type, validations etc.) of the request or
// response type attributes with identical names.
//
// Assuming the type:
//
// var CreatePayload = Type("CreatePayload", func() {
// Attribute("name", String, "Name of account")
// })
//
// The following:
//
// Method("create", func() {
// Payload(CreatePayload)
// })
//
// is equivalent to:
//
// Method("create", func() {
// Payload(CreatePayload)
// HTTP(func() {
// Body(func() {
// Attribute("name")
// })
// })
// })
//
func Body(args ...interface{}) {
if len(args) == 0 {
eval.ReportError("not enough arguments, use Body(name), Body(type), Body(func()) or Body(type, func())")
return
}
var (
ref *expr.AttributeExpr
setter func(*expr.AttributeExpr)
kind string
)
// Figure out reference type and setter function
switch e := eval.Current().(type) {
case *expr.HTTPEndpointExpr:
ref = e.MethodExpr.Payload
setter = func(att *expr.AttributeExpr) {
e.Body = att
}
kind = "Request"
case *expr.HTTPErrorExpr:
ref = e.ErrorExpr.AttributeExpr
setter = func(att *expr.AttributeExpr) {
if e.Response == nil {
e.Response = &expr.HTTPResponseExpr{}
}
e.Response.Body = att
}
kind = "Error"
if e.Name != "" {
kind += " " + e.Name
}
case *expr.HTTPResponseExpr:
ref = e.Parent.(*expr.HTTPEndpointExpr).MethodExpr.Result
setter = func(att *expr.AttributeExpr) {
e.Body = att
}
kind = "Response"
default:
eval.IncompatibleDSL()
return
}
// Now initialize target attribute and DSL if any
var (
attr *expr.AttributeExpr
fn func()
)
switch a := args[0].(type) {
case string:
if !expr.IsObject(ref.Type) {
eval.ReportError("%s type must be an object with an attribute with name %#v, got %T", kind, a, ref.Type)
return
}
attr = ref.Find(a)
if attr == nil {
eval.ReportError("%s type does not have an attribute named %#v", kind, a)
return
}
attr = expr.DupAtt(attr)
attr.AddMeta("origin:attribute", a)
if rt, ok := attr.Type.(*expr.ResultTypeExpr); ok {
// If the attribute type is a result type add the type to the
// GeneratedTypes so that the type's DSLFunc is executed.
*expr.Root.GeneratedTypes = append(*expr.Root.GeneratedTypes, rt)
}
if len(args) > 1 {
var ok bool
fn, ok = args[1].(func())
if !ok {
eval.ReportError("second argument must be a function")
}
}
case expr.UserType:
attr = &expr.AttributeExpr{Type: a}
if len(args) > 1 {
var ok bool
fn, ok = args[1].(func())
if !ok {
eval.ReportError("second argument must be a function")
}
}
case func():
fn = a
if ref == nil {
eval.ReportError("Body is set but Payload is not defined")
return
}
attr = &expr.AttributeExpr{References: []expr.DataType{ref.Type}}
default:
eval.InvalidArgError("attribute name, user type or DSL", a)
return
}
// Set body attribute
if fn != nil {
eval.Execute(fn, attr)
}
attr.AddMeta("http:body")
setter(attr)
}
// Parent sets the name of the parent service. The parent service canonical
// method path is used as prefix for all the service HTTP endpoint paths.
//
// Attributes of the parent method payload that map to parent path parameters
// are automatically merged into the child method payload type if not already
// defined.
//
// Parent must appear in the HTTP expresssion of a Service.
//
// Parent accepts one argument: the name of the parent service.
func Parent(name string) {
r, ok := eval.Current().(*expr.HTTPServiceExpr)
if !ok {
eval.IncompatibleDSL()
return
}
r.ParentName = name
}
// CanonicalMethod sets the name of the service canonical method. The canonical
// method endpoint HTTP path is used to prefix the paths to child service
// endpoints (a child service is a service that uses the Parent function). The
// default value is "show".
//
// CanonicalMethod must appear in the HTTP expresssion of a Service.
//
// CanonicalMethod accepts one argument: the name of the canonical service
// method.
func CanonicalMethod(name string) {
r, ok := eval.Current().(*expr.HTTPServiceExpr)
if !ok {
eval.IncompatibleDSL()
return
}
r.CanonicalEndpointName = name
}
// Tag identifies a method result type field and a value. The algorithm that
// encodes the result into the HTTP response iterates through the responses and
// uses the first response that has a matching tag (that is for which the result
// field with the tag name matches the tag value). There must be one and only
// one response with no Tag expression, this response is used when no other tag
// matches.
//
// Tag must appear in Response.
//
// Tag accepts two arguments: the name of the field and the (string) value.
//
// Example:
//
// Method("create", func() {
// Result(CreateResult)
// HTTP(func() {
// Response(StatusCreated, func() {
// Tag("outcome", "created") // Assumes CreateResult has attribute
// // "outcome" which may be "created"
// // or "accepted"
// })
//
// Response(StatusAccepted, func() {
// Tag("outcome", "accepted")
// })
//
// Response(StatusOK) // Default response if "outcome" is
// // neither "created" nor "accepted"
// })
// })
//
func Tag(name, value string) {
res, ok := eval.Current().(*expr.HTTPResponseExpr)
if !ok {
eval.IncompatibleDSL()
return
}
res.Tag = [2]string{name, value}
}
// ContentType sets the value of the Content-Type response header.
//
// ContentType must appear in a Response expression.
// ContentType accepts one argument: the mime type as defined by RFC 6838.
//
// var _ = Method("add", func() {
// HTTP(func() {
// Response(StatusOK, func() {
// ContentType("application/json")
// })
// })
// })
//
func ContentType(typ string) {
switch actual := eval.Current().(type) {
case *expr.ResultTypeExpr:
actual.ContentType = typ // deprecated
case *expr.HTTPResponseExpr:
actual.ContentType = typ
default:
eval.IncompatibleDSL()
}
}
// headers returns the mapped attribute containing the headers for the given
// expression if it's either the root, a service or an endpoint - nil otherwise.
func headers(exp eval.Expression) *expr.MappedAttributeExpr {
switch e := exp.(type) {
case *expr.RootExpr:
if e.API.HTTP.Headers == nil {
e.API.HTTP.Headers = expr.NewEmptyMappedAttributeExpr()
}
return e.API.HTTP.Headers
case *expr.HTTPServiceExpr:
if e.Headers == nil {
e.Headers = expr.NewEmptyMappedAttributeExpr()
}
return e.Headers
case *expr.HTTPEndpointExpr:
if e.Headers == nil {
e.Headers = expr.NewEmptyMappedAttributeExpr()
}
return e.Headers
case *expr.HTTPResponseExpr:
if e.Headers == nil {
e.Headers = expr.NewEmptyMappedAttributeExpr()
}
return e.Headers
case *expr.MappedAttributeExpr:
return e
default:
return nil
}
}
// cookies returns the mapped attribute containing the cookies for the given
// expression if it's either the root, a service or an endpoint - nil otherwise.
func cookies(exp eval.Expression) *expr.MappedAttributeExpr {
switch e := exp.(type) {
case *expr.RootExpr:
if e.API.HTTP.Cookies == nil {
e.API.HTTP.Cookies = expr.NewEmptyMappedAttributeExpr()
}
return e.API.HTTP.Cookies
case *expr.HTTPServiceExpr:
if e.Cookies == nil {
e.Cookies = expr.NewEmptyMappedAttributeExpr()
}
return e.Cookies
case *expr.HTTPEndpointExpr:
if e.Cookies == nil {
e.Cookies = expr.NewEmptyMappedAttributeExpr()
}
return e.Cookies
case *expr.HTTPResponseExpr:
if e.Cookies == nil {
e.Cookies = expr.NewEmptyMappedAttributeExpr()
}
return e.Cookies
case *expr.MappedAttributeExpr:
return e
default:
return nil
}
}
// params returns the mapped attribute containing the path and query params for
// the given expression if it's either the root, a API server, a service or an
// endpoint - nil otherwise.
func params(exp eval.Expression) *expr.MappedAttributeExpr {
switch e := exp.(type) {
case *expr.RootExpr:
if e.API.HTTP.Params == nil {
e.API.HTTP.Params = expr.NewEmptyMappedAttributeExpr()
}
return e.API.HTTP.Params
case *expr.HTTPServiceExpr:
if e.Params == nil {
e.Params = expr.NewEmptyMappedAttributeExpr()
}
return e.Params
case *expr.HTTPEndpointExpr:
if e.Params == nil {
e.Params = expr.NewEmptyMappedAttributeExpr()
}
return e.Params
case *expr.MappedAttributeExpr:
return e
default:
return nil
}
}
// cookieAttribute initialize the current attribute metadata with the details of
// a HTTP cookie attribute for use by the HTTP code generator.
func cookieAttribute(name, value string) {
c := eval.Current().(*expr.HTTPResponseExpr).Cookies
c.AddMeta("cookie:"+name, value)
}
1
https://gitee.com/mirrors/goa.git
git@gitee.com:mirrors/goa.git
mirrors
goa
goa
v2.2.5

搜索帮助