Repository: goadesign/goa
Pull request #2857 (2021-05-25 21:14:43)

Map unmapped attrs ErrorResult
* Only map attributes that are unmapped if type is ErrorResult (Undoes many of the changes in #2853 )


Patch:
diff --git a/expr/http_body_types.go b/expr/http_body_types.go
index 5e7d3c29..2c2007f2 100644
--- a/expr/http_body_types.go
+++ b/expr/http_body_types.go
@@ -93,6 +93,7 @@ func httpRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
 
 	// 2. Remove header, param and cookies attributes
 	body := NewMappedAttributeExpr(payload)
+	extendBodyAttribute(body)
 	removeAttributes(body, headers)
 	removeAttributes(body, cookies)
 	removeAttributes(body, params)
@@ -209,6 +210,7 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
 		return &AttributeExpr{Type: Empty}
 	}
 	body := NewMappedAttributeExpr(attr)
+	extendBodyAttribute(body)
 
 	// 2. Remove header and cookie attributes
 	removeAttributes(body, resp.Headers)
@@ -386,85 +388,33 @@ func removeAttribute(attr *MappedAttributeExpr, name string) {
 	}
 }
 
-// extendedHTTPRequestBody returns an attribute describing the HTTP request body.
-// This is used only in the validation phase to figure out the request body when
-// method Payload extends or references other types.
-func extendedHTTPRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
-	const suffix = "RequestBody"
-	var (
-		name = concat(a.Name(), "Request", "Body")
-	)
-	if a.Body != nil {
-		a.Body = DupAtt(a.Body)
-		renameType(a.Body, name, suffix)
-		return a.Body
-	}
-
-	var (
-		payload  = a.MethodExpr.Payload
-		headers  = a.Headers
-		params   = a.Params
-		bodyOnly = headers.IsEmpty() && params.IsEmpty() && a.MapQueryParams == nil
-	)
-
-	// 1. If Payload is not an object then check whether there are params or
-	// headers defined and if so return empty type (payload encoded in
-	// request params or headers) otherwise return payload type (payload
-	// encoded in request body).
-	if !IsObject(payload.Type) {
-		if bodyOnly {
-			payload = DupAtt(payload)
-			renameType(payload, name, suffix)
-			return payload
-		}
-		return &AttributeExpr{Type: Empty}
+// extendedBodyAttribute returns an attribute describing the HTTP
+// request/response body type by merging any Bases and References to the parent
+// attribute. This must be invoked during validation or to determine the actual
+// body type by removing any headers/params/cookies.
+func extendBodyAttribute(body *MappedAttributeExpr) {
+	att := body.AttributeExpr
+	if isEmpty(att) {
+		return
 	}
-
-	// Merge extended and referenced types
-	payload = DupAtt(payload)
-	for _, ref := range payload.References {
+	for _, ref := range att.References {
 		ru, ok := ref.(UserType)
 		if !ok {
 			continue
 		}
-		payload.Inherit(ru.Attribute())
+		att.Inherit(ru.Attribute())
 	}
-	for _, base := range payload.Bases {
+	// unset references so that they don't get added back to the body type during
+	// finalize
+	att.References = nil
+	for _, base := range att.Bases {
 		ru, ok := base.(UserType)
 		if !ok {
 			continue
 		}
-		payload.Merge(ru.Attribute())
-	}
-
-	// 2. Remove header and param attributes
-	body := NewMappedAttributeExpr(payload)
-	removeAttributes(body, headers)
-	removeAttributes(body, params)
-	if a.MapQueryParams != nil && *a.MapQueryParams != "" {
-		removeAttribute(body, *a.MapQueryParams)
-	}
-	for att := range defaultRequestHeaderAttributes(a) {
-		removeAttribute(body, att)
-	}
-
-	// 3. Return empty type if no attribute left
-	if len(*AsObject(body.Type)) == 0 {
-		return &AttributeExpr{Type: Empty}
-	}
-
-	// 4. Build computed user type
-	att := body.Attribute()
-	ut := &UserTypeExpr{
-		AttributeExpr: att,
-		TypeName:      name,
-		UID:           a.Service.Name() + "#" + a.Name(),
-	}
-	appendSuffix(ut.Attribute().Type, suffix)
-
-	return &AttributeExpr{
-		Type:         ut,
-		Validation:   att.Validation,
-		UserExamples: att.UserExamples,
+		att.Merge(ru.Attribute())
 	}
+	// unset bases so that they don't get added back to the body type during
+	// finalize
+	att.Bases = nil
 }
diff --git a/expr/http_endpoint.go b/expr/http_endpoint.go
index 4fa04bc1..4ea8c18d 100644
--- a/expr/http_endpoint.go
+++ b/expr/http_endpoint.go
@@ -608,7 +608,7 @@ func (e *HTTPEndpointExpr) Validate() error {
 		}
 	}
 
-	body := extendedHTTPRequestBody(e)
+	body := httpRequestBody(e)
 	if e.SkipRequestBodyEncodeDecode && body.Type != Empty {
 		verr.Add(e, "HTTP endpoint request body must be empty when using SkipRequestBodyEncodeDecode but not all method payload attributes are mapped to headers and params. Make sure to define Headers and Params as needed.")
 	}
@@ -674,28 +674,19 @@ func (e *HTTPEndpointExpr) Finalize() {
 	initAttr(e.Headers, e.MethodExpr.Payload)
 	initAttr(e.Cookies, e.MethodExpr.Payload)
 
-	if e.Body != nil {
-		// rename type to add RequestBody suffix so that we don't end with
-		// duplicate type definitions - https://github.com/goadesign/goa/issues/1969
-		e.Body = httpRequestBody(e)
-		e.Body.Finalize()
-	} else {
-		// Compute body from the payload expression
-		e.Body = httpRequestBody(e)
-		// Don't call e.Body.Finalize() after computing the body because the
-		// payload expression might define bases and references which will be
-		// added to the body even when design explicitly maps them to headers or
-		// params.
-	}
+	e.Body = httpRequestBody(e)
+	e.Body.Finalize()
 
 	e.StreamingBody = httpStreamingBody(e)
+	if e.StreamingBody != nil {
+		e.StreamingBody.Finalize()
+	}
 
 	// Initialize responses parent, headers and body
 	for _, r := range e.Responses {
 		r.Finalize(e, e.MethodExpr.Result)
 		r.Body = httpResponseBody(e, r)
 		r.Body.Finalize()
-		r.mapUnmappedAttrs(e.MethodExpr.Result)
 	}
 
 	// Make sure all error types are user types and have a body.
diff --git a/expr/http_error.go b/expr/http_error.go
index 9f9d8243..881dd400 100644
--- a/expr/http_error.go
+++ b/expr/http_error.go
@@ -99,7 +99,9 @@ func (e *HTTPErrorExpr) Finalize(a *HTTPEndpointExpr) {
 	e.Response.Finalize(a, e.AttributeExpr)
 	if e.Response.Body == nil {
 		e.Response.Body = httpErrorResponseBody(a, e)
+		e.Response.Body.Finalize()
 	}
+	// map any unmapped attributes in ErrorResult type to response headers
 	e.Response.mapUnmappedAttrs(e.AttributeExpr)
 
 	// Initialize response content type if result is media type.
diff --git a/expr/http_response.go b/expr/http_response.go
index fc01a596..638cf223 100644
--- a/expr/http_response.go
+++ b/expr/http_response.go
@@ -180,7 +180,7 @@ func (r *HTTPResponseExpr) Validate(e *HTTPEndpointExpr) *eval.ValidationErrors
 
 	if !r.Headers.IsEmpty() {
 		verr.Merge(r.Headers.Validate("HTTP response headers", r))
-		if e.MethodExpr.Result.Type == Empty {
+		if isEmpty(e.MethodExpr.Result) {
 			verr.Add(r, "response defines headers but result is empty")
 		} else if IsObject(e.MethodExpr.Result.Type) {
 			mobj := AsObject(r.Headers.Type)
@@ -206,7 +206,7 @@ func (r *HTTPResponseExpr) Validate(e *HTTPEndpointExpr) *eval.ValidationErrors
 	}
 	if !r.Cookies.IsEmpty() {
 		verr.Merge(r.Cookies.Validate("HTTP response cookies", r))
-		if e.MethodExpr.Result.Type == Empty {
+		if isEmpty(e.MethodExpr.Result) {
 			verr.Add(r, "response defines cookies but result is empty")
 		} else if IsObject(e.MethodExpr.Result.Type) {
 			mobj := AsObject(r.Cookies.Type)
@@ -255,47 +255,45 @@ func (r *HTTPResponseExpr) Validate(e *HTTPEndpointExpr) *eval.ValidationErrors
 func (r *HTTPResponseExpr) Finalize(a *HTTPEndpointExpr, svcAtt *AttributeExpr) {
 	r.Parent = a
 
-	if r.Body != nil {
-		if r.Body.Type != Empty {
-			bodyAtt := svcAtt
-			if o, ok := r.Body.Meta["origin:attribute"]; ok {
-				bodyAtt = svcAtt.Find(o[0])
-			}
-			bodyObj := AsObject(bodyAtt.Type)
-			if body := AsObject(r.Body.Type); body != nil {
-				for _, nat := range *body {
-					n := nat.Name
-					n = strings.Split(n, ":")[0]
-					var att, patt *AttributeExpr
-					var required bool
-					if bodyObj != nil {
-						att = bodyObj.Attribute(n)
-						required = bodyAtt.IsRequired(n)
-					} else {
-						att = bodyAtt
-						required = bodyAtt.Type != Empty
-					}
-					initAttrFromDesign(att, patt)
-					if required {
-						if r.Body.Validation == nil {
-							r.Body.Validation = &ValidationExpr{}
-						}
-						r.Body.Validation.AddRequired(n)
-					}
-				}
-				// Remember original name for example to generate friendlier OpenAPI specs.
-				if t, ok := r.Body.Type.(UserType); ok {
-					t.Attribute().AddMeta("name:original", t.Name())
+	if r.Body != nil && r.Body.Type != Empty {
+		bodyAtt := svcAtt
+		if o, ok := r.Body.Meta["origin:attribute"]; ok {
+			bodyAtt = svcAtt.Find(o[0])
+		}
+		bodyObj := AsObject(bodyAtt.Type)
+		if body := AsObject(r.Body.Type); body != nil {
+			for _, nat := range *body {
+				n := nat.Name
+				n = strings.Split(n, ":")[0]
+				var att, patt *AttributeExpr
+				var required bool
+				if bodyObj != nil {
+					att = bodyObj.Attribute(n)
+					required = bodyAtt.IsRequired(n)
+				} else {
+					att = bodyAtt
+					required = bodyAtt.Type != Empty
 				}
-				// Wrap object with user type to simplify response rendering code.
-				r.Body.Type = &UserTypeExpr{
-					AttributeExpr: DupAtt(r.Body),
-					TypeName:      fmt.Sprintf("%s%sResponseBody", a.Service.Name(), a.Name()),
+				initAttrFromDesign(att, patt)
+				if required {
+					if r.Body.Validation == nil {
+						r.Body.Validation = &ValidationExpr{}
+					}
+					r.Body.Validation.AddRequired(n)
 				}
 			}
-			if r.Body.Meta == nil {
-				r.Body.Meta = bodyAtt.Meta
+			// Remember original name for example to generate friendlier OpenAPI specs.
+			if t, ok := r.Body.Type.(UserType); ok {
+				t.Attribute().AddMeta("name:original", t.Name())
 			}
+			// Wrap object with user type to simplify response rendering code.
+			r.Body.Type = &UserTypeExpr{
+				AttributeExpr: DupAtt(r.Body),
+				TypeName:      fmt.Sprintf("%s%sResponseBody", a.Service.Name(), a.Name()),
+			}
+		}
+		if r.Body.Meta == nil {
+			r.Body.Meta = bodyAtt.Meta
 		}
 	}
 
@@ -331,13 +329,13 @@ func (r *HTTPResponseExpr) Dup() *HTTPResponseExpr {
 	return &res
 }
 
-// mapUnmappedAttrs maps any unmapped attributes to the response headers.
-// Unmapped attributes refer to the attributes in the given service type
-// (Result or Error) that are not mapped to response body, headers, cookies, or
-// tags. Such unmapped attributes are mapped to special goa headers in the form
-// of "Goa-Attribute(-<Attribute Name>)".
+// mapUnmappedAttrs maps any unmapped attributes in ErrorResult type to the
+// response headers. Unmapped attributes refer to the attributes in ErrorResult
+// type that are not mapped to response body or headers. Such unmapped
+// attributes are mapped to special goa headers in the form of
+// "Goa-Attribute(-<Attribute Name>)".
 func (r *HTTPResponseExpr) mapUnmappedAttrs(svcAtt *AttributeExpr) {
-	if svcAtt.Type == Empty {
+	if svcAtt.Type != ErrorResult {
 		return
 	}
 
@@ -365,12 +363,6 @@ func (r *HTTPResponseExpr) mapUnmappedAttrs(svcAtt *AttributeExpr) {
 				if _, ok := r.Headers.FindKey(nat.Name); ok {
 					continue
 				}
-				if _, ok := r.Cookies.FindKey(nat.Name); ok {
-					continue
-				}
-				if r.Tag[0] == nat.Name {
-					continue
-				}
 				r.Headers.Type.(*Object).Set(nat.Name, nat.Attribute)
 				r.Headers.Map("goa-attribute-"+nat.Name, nat.Name)
 				if svcAtt.IsRequired(nat.Name) {
@@ -382,7 +374,7 @@ func (r *HTTPResponseExpr) mapUnmappedAttrs(svcAtt *AttributeExpr) {
 			}
 		}
 	default:
-		if r.Headers.IsEmpty() && r.Cookies.IsEmpty() && (r.Body == nil || r.Body.Type == Empty) {
+		if r.Headers.IsEmpty() && (r.Body == nil || r.Body.Type == Empty) {
 			r.Headers.Type.(*Object).Set("goa-attribute", svcAtt)
 		}
 	}
diff --git a/go.mod b/go.mod
index baf265c3..3ee7c9af 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect
 	golang.org/x/tools v0.1.1
 	google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384 // indirect
-	google.golang.org/grpc v1.37.1
+	google.golang.org/grpc v1.38.0
 	google.golang.org/protobuf v1.26.0
 	gopkg.in/yaml.v2 v2.4.0
 )
diff --git a/go.sum b/go.sum
index 12aaf669..0737645e 100644
--- a/go.sum
+++ b/go.sum
@@ -392,8 +392,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=


Test patch:
diff --git a/http/codegen/client_body_types_test.go b/http/codegen/client_body_types_test.go
index f0618e63..f3c641dd 100644
--- a/http/codegen/client_body_types_test.go
+++ b/http/codegen/client_body_types_test.go
@@ -222,13 +222,12 @@ const ExplicitBodyPrimitiveResultMultipleViewsInitCode = `// NewMethodExplicitBo
 // builds a "ServiceExplicitBodyPrimitiveResultMultipleView" service
 // "MethodExplicitBodyPrimitiveResultMultipleView" endpoint result from a HTTP
 // "OK" response.
-func NewMethodExplicitBodyPrimitiveResultMultipleViewResulttypemultipleviewsOK(body string, c *string, b *string) *serviceexplicitbodyprimitiveresultmultipleviewviews.ResulttypemultipleviewsView {
+func NewMethodExplicitBodyPrimitiveResultMultipleViewResulttypemultipleviewsOK(body string, c *string) *serviceexplicitbodyprimitiveresultmultipleviewviews.ResulttypemultipleviewsView {
 	v := body
 	res := &serviceexplicitbodyprimitiveresultmultipleviewviews.ResulttypemultipleviewsView{
 		A: &v,
 	}
 	res.C = c
-	res.B = b
 
 	return res
 }
@@ -238,7 +237,7 @@ const ExplicitBodyUserResultMultipleViewsInitCode = `// NewMethodExplicitBodyUse
 // a "ServiceExplicitBodyUserResultMultipleView" service
 // "MethodExplicitBodyUserResultMultipleView" endpoint result from a HTTP "OK"
 // response.
-func NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultMultipleViewResponseBody, c *string, b *string) *serviceexplicitbodyuserresultmultipleviewviews.ResulttypemultipleviewsView {
+func NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultMultipleViewResponseBody, c *string) *serviceexplicitbodyuserresultmultipleviewviews.ResulttypemultipleviewsView {
 	v := &serviceexplicitbodyuserresultmultipleviewviews.UserTypeView{
 		X: body.X,
 		Y: body.Y,
@@ -247,7 +246,6 @@ func NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(body *
 		A: v,
 	}
 	res.C = c
-	res.B = b
 
 	return res
 }
diff --git a/http/codegen/client_decode_test.go b/http/codegen/client_decode_test.go
index 38710887..af565db0 100644
--- a/http/codegen/client_decode_test.go
+++ b/http/codegen/client_decode_test.go
@@ -31,14 +31,6 @@ func TestClientDecode(t *testing.T) {
 		{"with-headers-dsl-viewed-result", testdata.WithHeadersBlockViewedResultDSL, testdata.WithHeadersBlockViewedResultResponseDecodeCode},
 		{"validate-error-response-type", testdata.ValidateErrorResponseTypeDSL, testdata.ValidateErrorResponseTypeDecodeCode},
 		{"empty-error-response-body", testdata.EmptyErrorResponseBodyDSL, testdata.EmptyErrorResponseBodyDecodeCode},
-
-		{"unmapped-result-object", testdata.UnmappedResultObjectDSL, testdata.UnmappedResultObjectDecodeCode},
-		{"unmapped-result-object-validate", testdata.UnmappedResultObjectValidateDSL, testdata.UnmappedResultObjectValidateDecodeCode},
-		{"unmapped-result-object-with-headers", testdata.UnmappedResultObjectWithHeadersDSL, testdata.UnmappedResultObjectWithHeadersDecodeCode},
-		{"unmapped-result-primitive", testdata.UnmappedResultPrimitiveDSL, testdata.UnmappedResultPrimitiveDecodeCode},
-		{"unmapped-result-primitive-validate", testdata.UnmappedResultPrimitiveValidateDSL, testdata.UnmappedResultPrimitiveValidateDecodeCode},
-		{"unmapped-result-array", testdata.UnmappedResultArrayDSL, testdata.UnmappedResultArrayDecodeCode},
-		{"unmapped-result-array-validate", testdata.UnmappedResultArrayValidateDSL, testdata.UnmappedResultArrayValidateDecodeCode},
 	}
 	for _, c := range cases {
 		t.Run(c.Name, func(t *testing.T) {
diff --git a/http/codegen/server_encode_test.go b/http/codegen/server_encode_test.go
index 52412410..18457b76 100644
--- a/http/codegen/server_encode_test.go
+++ b/http/codegen/server_encode_test.go
@@ -80,15 +80,6 @@ func TestEncode(t *testing.T) {
 
 		{"empty-server-response", testdata.EmptyServerResponseDSL, testdata.EmptyServerResponseEncodeCode},
 		{"empty-server-response-with-tags", testdata.EmptyServerResponseWithTagsDSL, testdata.EmptyServerResponseWithTagsEncodeCode},
-
-		{"unmapped-result-object", testdata.UnmappedResultObjectDSL, testdata.UnmappedResultObjectEncodeCode},
-		{"unmapped-result-object-with-body", testdata.UnmappedResultObjectWithBodyDSL, testdata.UnmappedResultObjectWithBodyEncodeCode},
-		{"unmapped-result-object-validate", testdata.UnmappedResultObjectValidateDSL, testdata.UnmappedResultObjectValidateEncodeCode},
-		{"unmapped-result-object-with-headers", testdata.UnmappedResultObjectWithHeadersDSL, testdata.UnmappedResultObjectWithHeadersEncodeCode},
-		{"unmapped-result-primitive", testdata.UnmappedResultPrimitiveDSL, testdata.UnmappedResultPrimitiveEncodeCode},
-		{"unmapped-result-primitive-validate", testdata.UnmappedResultPrimitiveValidateDSL, testdata.UnmappedResultPrimitiveValidateEncodeCode},
-		{"unmapped-result-array", testdata.UnmappedResultArrayDSL, testdata.UnmappedResultArrayEncodeCode},
-		{"unmapped-result-array-validate", testdata.UnmappedResultArrayValidateDSL, testdata.UnmappedResultArrayValidateEncodeCode},
 	}
 	for _, c := range cases {
 		t.Run(c.Name, func(t *testing.T) {
diff --git a/http/codegen/server_error_encoder_test.go b/http/codegen/server_error_encoder_test.go
index 413b5378..445d2ee0 100644
--- a/http/codegen/server_error_encoder_test.go
+++ b/http/codegen/server_error_encoder_test.go
@@ -26,6 +26,7 @@ func TestEncodeError(t *testing.T) {
 		{"api-no-body-error-response", testdata.APINoBodyErrorResponseDSL, testdata.NoBodyErrorResponseEncoderCode},
 		{"api-no-body-error-response-with-content-type", testdata.APINoBodyErrorResponseWithContentTypeDSL, testdata.NoBodyErrorResponseWithContentTypeEncoderCode},
 		{"empty-error-response-body", testdata.EmptyErrorResponseBodyDSL, testdata.EmptyErrorResponseBodyEncoderCode},
+		{"empty-custom-error-response-body", testdata.EmptyCustomErrorResponseBodyDSL, testdata.EmptyCustomErrorResponseBodyEncoderCode},
 	}
 	for _, c := range cases {
 		t.Run(c.Name, func(t *testing.T) {
diff --git a/http/codegen/testdata/error_encoder_code.go b/http/codegen/testdata/error_encoder_code.go
index 0448b370..7c675d41 100644
--- a/http/codegen/testdata/error_encoder_code.go
+++ b/http/codegen/testdata/error_encoder_code.go
@@ -323,3 +323,26 @@ func EncodeMethodEmptyErrorResponseBodyError(encoder func(context.Context, http.
 	}
 }
 `
+
+var EmptyCustomErrorResponseBodyEncoderCode = `// EncodeMethodEmptyCustomErrorResponseBodyError returns an encoder for errors
+// returned by the MethodEmptyCustomErrorResponseBody
+// ServiceEmptyCustomErrorResponseBody endpoint.
+func EncodeMethodEmptyCustomErrorResponseBodyError(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, formatter func(err error) goahttp.Statuser) func(context.Context, http.ResponseWriter, error) error {
+	encodeError := goahttp.ErrorEncoder(encoder, formatter)
+	return func(ctx context.Context, w http.ResponseWriter, v error) error {
+		en, ok := v.(ErrorNamer)
+		if !ok {
+			return encodeError(ctx, w, v)
+		}
+		switch en.ErrorName() {
+		case "internal_error":
+			res := v.(*serviceemptycustomerrorresponsebody.Error)
+			w.Header().Set("goa-error", res.ErrorName())
+			w.WriteHeader(http.StatusInternalServerError)
+			return nil
+		default:
+			return encodeError(ctx, w, v)
+		}
+	}
+}
+`
diff --git a/http/codegen/testdata/payload_dsls.go b/http/codegen/testdata/payload_dsls.go
index 32d519dc..1210f484 100644
--- a/http/codegen/testdata/payload_dsls.go
+++ b/http/codegen/testdata/payload_dsls.go
@@ -2273,11 +2273,7 @@ var PayloadBodyPrimitiveFieldArrayUserDSL = func() {
 }
 
 var PayloadExtendBodyPrimitiveFieldArrayUserDSL = func() {
-	var Ext = Type("Ext", func() {
-		Attribute("b", String)
-	})
 	var PayloadType = Type("PayloadType", func() {
-		Extend(Ext)
 		Attribute("a", ArrayOf(String))
 	})
 	Service("ServiceBodyPrimitiveArrayUser", func() {
@@ -2297,7 +2293,6 @@ var PayloadExtendBodyPrimitiveFieldStringDSL = func() {
 	})
 	var PayloadType = Type("PayloadType", func() {
 		Extend(Ext)
-		Attribute("a", ArrayOf(String))
 	})
 	Service("ServiceBodyPrimitiveArrayUser", func() {
 		Method("MethodBodyPrimitiveArrayUser", func() {
diff --git a/http/codegen/testdata/result_decode_functions.go b/http/codegen/testdata/result_decode_functions.go
index 8b635637..21cd72e0 100644
--- a/http/codegen/testdata/result_decode_functions.go
+++ b/http/codegen/testdata/result_decode_functions.go
@@ -20,19 +20,7 @@ func DecodeMethodEmptyServerResponseResponse(decoder func(*http.Response) goahtt
 		}
 		switch resp.StatusCode {
 		case http.StatusOK:
-			var (
-				h   string
-				err error
-			)
-			hRaw := resp.Header.Get("Goa-Attribute-H")
-			if hRaw == "" {
-				err = goa.MergeErrors(err, goa.MissingFieldError("goa-attribute-h", "header"))
-			}
-			h = hRaw
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceEmptyServerResponse", "MethodEmptyServerResponse", err)
-			}
-			res := NewMethodEmptyServerResponseResultOK(h)
+			res := NewMethodEmptyServerResponseResultOK()
 			return res, nil
 		default:
 			body, _ := ioutil.ReadAll(resp.Body)
@@ -115,22 +103,12 @@ func DecodeMethodEmptyBodyResultMultipleViewResponse(decoder func(*http.Response
 		case http.StatusOK:
 			var (
 				c *string
-				a *string
-				b *string
 			)
 			cRaw := resp.Header.Get("Location")
 			if cRaw != "" {
 				c = &cRaw
 			}
-			aRaw := resp.Header.Get("Goa-Attribute-A")
-			if aRaw != "" {
-				a = &aRaw
-			}
-			bRaw := resp.Header.Get("Goa-Attribute-B")
-			if bRaw != "" {
-				b = &bRaw
-			}
-			p := NewMethodEmptyBodyResultMultipleViewResulttypemultipleviewsOK(c, a, b)
+			p := NewMethodEmptyBodyResultMultipleViewResulttypemultipleviewsOK(c)
 			view := resp.Header.Get("goa-view")
 			vres := &serviceemptybodyresultmultipleviewviews.Resulttypemultipleviews{Projected: p, View: view}
 			res := serviceemptybodyresultmultipleview.NewResulttypemultipleviews(vres)
@@ -180,17 +158,12 @@ func DecodeMethodExplicitBodyPrimitiveResultMultipleViewResponse(decoder func(*h
 			}
 			var (
 				c *string
-				b *string
 			)
 			cRaw := resp.Header.Get("Location")
 			if cRaw != "" {
 				c = &cRaw
 			}
-			bRaw := resp.Header.Get("Goa-Attribute-B")
-			if bRaw != "" {
-				b = &bRaw
-			}
-			p := NewMethodExplicitBodyPrimitiveResultMultipleViewResulttypemultipleviewsOK(body, c, b)
+			p := NewMethodExplicitBodyPrimitiveResultMultipleViewResulttypemultipleviewsOK(body, c)
 			view := resp.Header.Get("goa-view")
 			vres := &serviceexplicitbodyprimitiveresultmultipleviewviews.Resulttypemultipleviews{Projected: p, View: view}
 			if err = serviceexplicitbodyprimitiveresultmultipleviewviews.ValidateResulttypemultipleviews(vres); err != nil {
@@ -236,17 +209,12 @@ func DecodeMethodExplicitBodyUserResultMultipleViewResponse(decoder func(*http.R
 			}
 			var (
 				c *string
-				b *string
 			)
 			cRaw := resp.Header.Get("Location")
 			if cRaw != "" {
 				c = &cRaw
 			}
-			bRaw := resp.Header.Get("Goa-Attribute-B")
-			if bRaw != "" {
-				b = &bRaw
-			}
-			p := NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(&body, c, b)
+			p := NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(&body, c)
 			view := resp.Header.Get("goa-view")
 			vres := &serviceexplicitbodyuserresultmultipleviewviews.Resulttypemultipleviews{Projected: p, View: view}
 			if err = serviceexplicitbodyuserresultmultipleviewviews.ValidateResulttypemultipleviews(vres); err != nil {
@@ -398,19 +366,7 @@ func DecodeMethodEmptyServerResponseWithTagsResponse(decoder func(*http.Response
 			res.H = "true"
 			return res, nil
 		case http.StatusNoContent:
-			var (
-				h   string
-				err error
-			)
-			hRaw := resp.Header.Get("Goa-Attribute-H")
-			if hRaw == "" {
-				err = goa.MergeErrors(err, goa.MissingFieldError("goa-attribute-h", "header"))
-			}
-			h = hRaw
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceEmptyServerResponseWithTags", "MethodEmptyServerResponseWithTags", err)
-			}
-			res := NewMethodEmptyServerResponseWithTagsResultNoContent(h)
+			res := NewMethodEmptyServerResponseWithTagsResultNoContent()
 			return res, nil
 		default:
 			body, _ := ioutil.ReadAll(resp.Body)
@@ -978,454 +934,3 @@ func DecodeMethodEmptyErrorResponseBodyResponse(decoder func(*http.Response) goa
 	}
 }
 `
-
-var UnmappedResultObjectDecodeCode = `// DecodeMethodUnmappedResultObjectResponse returns a decoder for responses
-// returned by the ServiceUnmappedResultObject MethodUnmappedResultObject
-// endpoint. restoreBody controls whether the response body should be restored
-// after having been read.
-func DecodeMethodUnmappedResultObjectResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				hstr   *string
-				hint32 *int32
-				err    error
-			)
-			hstrRaw := resp.Header.Get("Goa-Attribute-Hstr")
-			if hstrRaw != "" {
-				hstr = &hstrRaw
-			}
-			{
-				hint32Raw := resp.Header.Get("Goa-Attribute-Hint32")
-				if hint32Raw != "" {
-					v, err2 := strconv.ParseInt(hint32Raw, 10, 32)
-					if err2 != nil {
-						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("hint32", hint32Raw, "integer"))
-					}
-					pv := int32(v)
-					hint32 = &pv
-				}
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultObject", "MethodUnmappedResultObject", err)
-			}
-			res := NewMethodUnmappedResultObjectResultOK(hstr, hint32)
-			return res, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultObject", "MethodUnmappedResultObject", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultObjectValidateDecodeCode = `// DecodeMethodUnmappedResultObjectValidateResponse returns a decoder for
-// responses returned by the ServiceUnmappedResultObjectValidate
-// MethodUnmappedResultObjectValidate endpoint. restoreBody controls whether
-// the response body should be restored after having been read.
-func DecodeMethodUnmappedResultObjectValidateResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				hstr   *string
-				hint32 *int32
-				err    error
-			)
-			hstrRaw := resp.Header.Get("Goa-Attribute-Hstr")
-			if hstrRaw != "" {
-				hstr = &hstrRaw
-			}
-			if hstr != nil {
-				err = goa.MergeErrors(err, goa.ValidateFormat("hstr", *hstr, goa.FormatDateTime))
-			}
-			{
-				hint32Raw := resp.Header.Get("Goa-Attribute-Hint32")
-				if hint32Raw != "" {
-					v, err2 := strconv.ParseInt(hint32Raw, 10, 32)
-					if err2 != nil {
-						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("hint32", hint32Raw, "integer"))
-					}
-					pv := int32(v)
-					hint32 = &pv
-				}
-			}
-			if hint32 != nil {
-				if *hint32 < 5 {
-					err = goa.MergeErrors(err, goa.InvalidRangeError("hint32", *hint32, 5, true))
-				}
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultObjectValidate", "MethodUnmappedResultObjectValidate", err)
-			}
-			res := NewMethodUnmappedResultObjectValidateResultOK(hstr, hint32)
-			return res, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultObjectValidate", "MethodUnmappedResultObjectValidate", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultObjectWithHeadersDecodeCode = `// DecodeMethodUnmappedResultObjectWithHeadersResponse returns a decoder for
-// responses returned by the ServiceUnmappedResultObjectWithHeaders
-// MethodUnmappedResultObjectWithHeaders endpoint. restoreBody controls whether
-// the response body should be restored after having been read.
-func DecodeMethodUnmappedResultObjectWithHeadersResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				hint32 *int32
-				hstr   *string
-				err    error
-			)
-			{
-				hint32Raw := resp.Header.Get("Location")
-				if hint32Raw != "" {
-					v, err2 := strconv.ParseInt(hint32Raw, 10, 32)
-					if err2 != nil {
-						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("hint32", hint32Raw, "integer"))
-					}
-					pv := int32(v)
-					hint32 = &pv
-				}
-			}
-			hstrRaw := resp.Header.Get("Goa-Attribute-Hstr")
-			if hstrRaw != "" {
-				hstr = &hstrRaw
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultObjectWithHeaders", "MethodUnmappedResultObjectWithHeaders", err)
-			}
-			res := NewMethodUnmappedResultObjectWithHeadersResultOK(hint32, hstr)
-			return res, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultObjectWithHeaders", "MethodUnmappedResultObjectWithHeaders", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultPrimitiveDecodeCode = `// DecodeMethodUnmappedResultPrimitiveResponse returns a decoder for responses
-// returned by the ServiceUnmappedResultPrimitive MethodUnmappedResultPrimitive
-// endpoint. restoreBody controls whether the response body should be restored
-// after having been read.
-func DecodeMethodUnmappedResultPrimitiveResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				goaAttribute *float32
-				err          error
-			)
-			{
-				goaAttributeRaw := resp.Header.Get("Goa-Attribute")
-				if goaAttributeRaw != "" {
-					v, err2 := strconv.ParseFloat(goaAttributeRaw, 32)
-					if err2 != nil {
-						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("goaAttribute", goaAttributeRaw, "float"))
-					}
-					pv := float32(v)
-					goaAttribute = &pv
-				}
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultPrimitive", "MethodUnmappedResultPrimitive", err)
-			}
-			return goaAttribute, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultPrimitive", "MethodUnmappedResultPrimitive", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultPrimitiveValidateDecodeCode = `// DecodeMethodUnmappedResultPrimitiveValidateResponse returns a decoder for
-// responses returned by the ServiceUnmappedResultPrimitiveValidate
-// MethodUnmappedResultPrimitiveValidate endpoint. restoreBody controls whether
-// the response body should be restored after having been read.
-func DecodeMethodUnmappedResultPrimitiveValidateResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				goaAttribute *float32
-				err          error
-			)
-			{
-				goaAttributeRaw := resp.Header.Get("Goa-Attribute")
-				if goaAttributeRaw != "" {
-					v, err2 := strconv.ParseFloat(goaAttributeRaw, 32)
-					if err2 != nil {
-						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("goaAttribute", goaAttributeRaw, "float"))
-					}
-					pv := float32(v)
-					goaAttribute = &pv
-				}
-			}
-			if goaAttribute != nil {
-				if *goaAttribute > 5 {
-					err = goa.MergeErrors(err, goa.InvalidRangeError("goaAttribute", *goaAttribute, 5, false))
-				}
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultPrimitiveValidate", "MethodUnmappedResultPrimitiveValidate", err)
-			}
-			return goaAttribute, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultPrimitiveValidate", "MethodUnmappedResultPrimitiveValidate", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultPrimitiveWithHeadersDecodeCode = `// DecodeMethodUnmappedResultPrimitiveWithHeadersResponse returns a decoder
-// for responses returned by the ServiceUnmappedResultPrimitiveWithHeaders
-// MethodUnmappedResultPrimitiveWithHeaders endpoint. restoreBody controls
-// whether the response body should be restored after having been read.
-func DecodeMethodUnmappedResultPrimitiveWithHeadersResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				location interface{}
-				err      error
-			)
-			locationRaw := resp.Header.Get("Location")
-			if locationRaw == "" {
-				err = goa.MergeErrors(err, goa.MissingFieldError("Location", "header"))
-			}
-			location = locationRaw
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultPrimitiveWithHeaders", "MethodUnmappedResultPrimitiveWithHeaders", err)
-			}
-			return location, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultPrimitiveWithHeaders", "MethodUnmappedResultPrimitiveWithHeaders", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultArrayDecodeCode = `// DecodeMethodUnmappedResultArrayResponse returns a decoder for responses
-// returned by the ServiceUnmappedResultArray MethodUnmappedResultArray
-// endpoint. restoreBody controls whether the response body should be restored
-// after having been read.
-func DecodeMethodUnmappedResultArrayResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				goaAttribute []uint
-				err          error
-			)
-			{
-				goaAttributeRaw := resp.Header["Goa-Attribute"]
-
-				if goaAttributeRaw != nil {
-					goaAttribute = make([]uint, len(goaAttributeRaw))
-					for i, rv := range goaAttributeRaw {
-						v, err2 := strconv.ParseUint(rv, 10, strconv.IntSize)
-						if err2 != nil {
-							err = goa.MergeErrors(err, goa.InvalidFieldTypeError("goaAttribute", goaAttributeRaw, "array of unsigned integers"))
-						}
-						goaAttribute[i] = uint(v)
-					}
-				}
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultArray", "MethodUnmappedResultArray", err)
-			}
-			return goaAttribute, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultArray", "MethodUnmappedResultArray", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultArrayValidateDecodeCode = `// DecodeMethodUnmappedResultArrayValidateResponse returns a decoder for
-// responses returned by the ServiceUnmappedResultArrayValidate
-// MethodUnmappedResultArrayValidate endpoint. restoreBody controls whether the
-// response body should be restored after having been read.
-func DecodeMethodUnmappedResultArrayValidateResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				goaAttribute []uint
-				err          error
-			)
-			{
-				goaAttributeRaw := resp.Header["Goa-Attribute"]
-
-				if goaAttributeRaw != nil {
-					goaAttribute = make([]uint, len(goaAttributeRaw))
-					for i, rv := range goaAttributeRaw {
-						v, err2 := strconv.ParseUint(rv, 10, strconv.IntSize)
-						if err2 != nil {
-							err = goa.MergeErrors(err, goa.InvalidFieldTypeError("goaAttribute", goaAttributeRaw, "array of unsigned integers"))
-						}
-						goaAttribute[i] = uint(v)
-					}
-				}
-			}
-			if len(goaAttribute) < 10 {
-				err = goa.MergeErrors(err, goa.InvalidLengthError("goaAttribute", goaAttribute, len(goaAttribute), 10, true))
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultArrayValidate", "MethodUnmappedResultArrayValidate", err)
-			}
-			return goaAttribute, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultArrayValidate", "MethodUnmappedResultArrayValidate", resp.StatusCode, string(body))
-		}
-	}
-}
-`
-
-var UnmappedResultArrayWithHeadersDecodeCode = `// DecodeMethodUnmappedResultArrayWithHeadersResponse returns a decoder for
-// responses returned by the ServiceUnmappedResultArrayWithHeaders
-// MethodUnmappedResultArrayWithHeaders endpoint. restoreBody controls
-// whether the response body should be restored after having been read.
-func DecodeMethodUnmappedResultArrayWithHeadersResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
-	return func(resp *http.Response) (interface{}, error) {
-		if restoreBody {
-			b, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return nil, err
-			}
-			resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			defer func() {
-				resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
-			}()
-		} else {
-			defer resp.Body.Close()
-		}
-		switch resp.StatusCode {
-		case http.StatusOK:
-			var (
-				location []string
-				err      error
-			)
-			location = resp.Header["Location"]
-
-			if location == nil {
-				err = goa.MergeErrors(err, goa.MissingFieldError("Location", "header"))
-			}
-			if err != nil {
-				return nil, goahttp.ErrValidationError("ServiceUnmappedResultArrayWithHeaders", "MethodUnmappedResultArrayWithHeaders", err)
-			}
-			return location, nil
-		default:
-			body, _ := ioutil.ReadAll(resp.Body)
-			return nil, goahttp.ErrInvalidResponse("ServiceUnmappedResultArrayWithHeaders", "MethodUnmappedResultArrayWithHeaders", resp.StatusCode, string(body))
-		}
-	}
-}
-`
diff --git a/http/codegen/testdata/result_dsls.go b/http/codegen/testdata/result_dsls.go
index 45b611c1..ecb153f9 100644
--- a/http/codegen/testdata/result_dsls.go
+++ b/http/codegen/testdata/result_dsls.go
@@ -765,6 +765,24 @@ var EmptyErrorResponseBodyDSL = func() {
 	})
 }
 
+var EmptyCustomErrorResponseBodyDSL = func() {
+	var ErrorType = Type("Error", func() {
+		Attribute("err", String)
+	})
+	Service("ServiceEmptyCustomErrorResponseBody", func() {
+		Method("MethodEmptyCustomErrorResponseBody", func() {
+			Error("internal_error", ErrorType)
+			HTTP(func() {
+				HEAD("/")
+				Response(StatusOK)
+				Response("internal_error", StatusInternalServerError, func() {
+					Body(Empty)
+				})
+			})
+		})
+	})
+}
+
 var ResultWithResultViewDSL = func() {
 	var RT = ResultType("RT", func() {
 		Attributes(func() {
@@ -1277,139 +1295,6 @@ var EmptyServerResponseWithTagsDSL = func() {
 	})
 }
 
-var UnmappedResultObjectDSL = func() {
-	Service("ServiceUnmappedResultObject", func() {
-		Method("MethodUnmappedResultObject", func() {
-			Result(func() {
-				Attribute("hstr", String)
-				Attribute("hint32", Int32)
-			})
-			HTTP(func() {
-				POST("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultObjectWithBodyDSL = func() {
-	Service("ServiceUnmappedResultObjectWithBody", func() {
-		Method("MethodUnmappedResultObjectWithBody", func() {
-			Result(func() {
-				Attribute("hstr", String)
-				Attribute("hint32", Int32)
-			})
-			HTTP(func() {
-				POST("/")
-				Response(StatusOK, func() {
-					Body("hstr")
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultObjectValidateDSL = func() {
-	Service("ServiceUnmappedResultObjectValidate", func() {
-		Method("MethodUnmappedResultObjectValidate", func() {
-			Result(func() {
-				Attribute("hstr", String, func() {
-					Format(FormatDateTime)
-				})
-				Attribute("hint32", Int32, func() {
-					Minimum(5)
-				})
-			})
-			HTTP(func() {
-				DELETE("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultObjectWithHeadersDSL = func() {
-	Service("ServiceUnmappedResultObjectWithHeaders", func() {
-		Method("MethodUnmappedResultObjectWithHeaders", func() {
-			Result(func() {
-				Attribute("hstr", String)
-				Attribute("hint32", Int32)
-			})
-			HTTP(func() {
-				HEAD("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-					Header("hint32:Location")
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultPrimitiveDSL = func() {
-	Service("ServiceUnmappedResultPrimitive", func() {
-		Method("MethodUnmappedResultPrimitive", func() {
-			Result(Float32)
-			HTTP(func() {
-				HEAD("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultPrimitiveValidateDSL = func() {
-	Service("ServiceUnmappedResultPrimitiveValidate", func() {
-		Method("MethodUnmappedResultPrimitiveValidate", func() {
-			Result(Float32, func() {
-				Maximum(5.0)
-			})
-			HTTP(func() {
-				GET("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultArrayDSL = func() {
-	Service("ServiceUnmappedResultArray", func() {
-		Method("MethodUnmappedResultArray", func() {
-			Result(ArrayOf(UInt))
-			HTTP(func() {
-				POST("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
-var UnmappedResultArrayValidateDSL = func() {
-	Service("ServiceUnmappedResultArrayValidate", func() {
-		Method("MethodUnmappedResultArrayValidate", func() {
-			Result(ArrayOf(UInt), func() {
-				MinLength(10)
-			})
-			HTTP(func() {
-				OPTIONS("/")
-				Response(StatusOK, func() {
-					Body(Empty)
-				})
-			})
-		})
-	})
-}
-
 var ResultHeaderStringImplicitDSL = func() {
 	Service("ServiceHeaderStringImplicit", func() {
 		Method("MethodHeaderStringImplicit", func() {
diff --git a/http/codegen/testdata/result_encode_functions.go b/http/codegen/testdata/result_encode_functions.go
index 70638086..14f13b44 100644
--- a/http/codegen/testdata/result_encode_functions.go
+++ b/http/codegen/testdata/result_encode_functions.go
@@ -688,12 +688,6 @@ func EncodeMethodEmptyBodyResultMultipleViewResponse(encoder func(context.Contex
 		if res.Projected.C != nil {
 			w.Header().Set("Location", *res.Projected.C)
 		}
-		if res.Projected.A != nil {
-			w.Header().Set("Goa-Attribute-A", *res.Projected.A)
-		}
-		if res.Projected.B != nil {
-			w.Header().Set("Goa-Attribute-B", *res.Projected.B)
-		}
 		w.WriteHeader(http.StatusOK)
 		return nil
 	}
@@ -739,9 +733,6 @@ func EncodeMethodExplicitBodyPrimitiveResultMultipleViewResponse(encoder func(co
 		if res.Projected.C != nil {
 			w.Header().Set("Location", *res.Projected.C)
 		}
-		if res.Projected.B != nil {
-			w.Header().Set("Goa-Attribute-B", *res.Projected.B)
-		}
 		w.WriteHeader(http.StatusOK)
 		return enc.Encode(body)
 	}
@@ -760,9 +751,6 @@ func EncodeMethodExplicitBodyUserResultMultipleViewResponse(encoder func(context
 		if res.Projected.C != nil {
 			w.Header().Set("Location", *res.Projected.C)
 		}
-		if res.Projected.B != nil {
-			w.Header().Set("Goa-Attribute-B", *res.Projected.B)
-		}
 		w.WriteHeader(http.StatusOK)
 		return enc.Encode(body)
 	}
@@ -1013,8 +1001,6 @@ var EmptyServerResponseEncodeCode = `// EncodeMethodEmptyServerResponseResponse
 // endpoint.
 func EncodeMethodEmptyServerResponseResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
 	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(*serviceemptyserverresponse.MethodEmptyServerResponseResult)
-		w.Header().Set("Goa-Attribute-H", res.H)
 		w.WriteHeader(http.StatusOK)
 		return nil
 	}
@@ -1031,201 +1017,8 @@ func EncodeMethodEmptyServerResponseWithTagsResponse(encoder func(context.Contex
 			w.WriteHeader(http.StatusNotModified)
 			return nil
 		}
-		w.Header().Set("Goa-Attribute-H", res.H)
 		w.WriteHeader(http.StatusNoContent)
 		return nil
 	}
 }
 `
-
-var UnmappedResultObjectEncodeCode = `// EncodeMethodUnmappedResultObjectResponse returns an encoder for responses
-// returned by the ServiceUnmappedResultObject MethodUnmappedResultObject
-// endpoint.
-func EncodeMethodUnmappedResultObjectResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(*serviceunmappedresultobject.MethodUnmappedResultObjectResult)
-		if res.Hstr != nil {
-			w.Header().Set("Goa-Attribute-Hstr", *res.Hstr)
-		}
-		if res.Hint32 != nil {
-			val := res.Hint32
-			hint32s := strconv.FormatInt(int64(*val), 10)
-			w.Header().Set("Goa-Attribute-Hint32", hint32s)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultObjectWithBodyEncodeCode = `// EncodeMethodUnmappedResultObjectWithBodyResponse returns an encoder for
-// responses returned by the ServiceUnmappedResultObjectWithBody
-// MethodUnmappedResultObjectWithBody endpoint.
-func EncodeMethodUnmappedResultObjectWithBodyResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(*serviceunmappedresultobjectwithbody.MethodUnmappedResultObjectWithBodyResult)
-		enc := encoder(ctx, w)
-		body := res.Hstr
-		if res.Hint32 != nil {
-			val := res.Hint32
-			hint32s := strconv.FormatInt(int64(*val), 10)
-			w.Header().Set("Goa-Attribute-Hint32", hint32s)
-		}
-		w.WriteHeader(http.StatusOK)
-		return enc.Encode(body)
-	}
-}
-`
-
-var UnmappedResultObjectValidateEncodeCode = `// EncodeMethodUnmappedResultObjectValidateResponse returns an encoder for
-// responses returned by the ServiceUnmappedResultObjectValidate
-// MethodUnmappedResultObjectValidate endpoint.
-func EncodeMethodUnmappedResultObjectValidateResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(*serviceunmappedresultobjectvalidate.MethodUnmappedResultObjectValidateResult)
-		if res.Hstr != nil {
-			w.Header().Set("Goa-Attribute-Hstr", *res.Hstr)
-		}
-		if res.Hint32 != nil {
-			val := res.Hint32
-			hint32s := strconv.FormatInt(int64(*val), 10)
-			w.Header().Set("Goa-Attribute-Hint32", hint32s)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultObjectWithHeadersEncodeCode = `// EncodeMethodUnmappedResultObjectWithHeadersResponse returns an encoder for
-// responses returned by the ServiceUnmappedResultObjectWithHeaders
-// MethodUnmappedResultObjectWithHeaders endpoint.
-func EncodeMethodUnmappedResultObjectWithHeadersResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(*serviceunmappedresultobjectwithheaders.MethodUnmappedResultObjectWithHeadersResult)
-		if res.Hint32 != nil {
-			val := res.Hint32
-			hint32s := strconv.FormatInt(int64(*val), 10)
-			w.Header().Set("Location", hint32s)
-		}
-		if res.Hstr != nil {
-			w.Header().Set("Goa-Attribute-Hstr", *res.Hstr)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultPrimitiveEncodeCode = `// EncodeMethodUnmappedResultPrimitiveResponse returns an encoder for responses
-// returned by the ServiceUnmappedResultPrimitive MethodUnmappedResultPrimitive
-// endpoint.
-func EncodeMethodUnmappedResultPrimitiveResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(float32)
-		{
-			val := res
-			goaAttributes := strconv.FormatFloat(float64(val), 'f', -1, 32)
-			w.Header().Set("Goa-Attribute", goaAttributes)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultPrimitiveValidateEncodeCode = `// EncodeMethodUnmappedResultPrimitiveValidateResponse returns an encoder for
-// responses returned by the ServiceUnmappedResultPrimitiveValidate
-// MethodUnmappedResultPrimitiveValidate endpoint.
-func EncodeMethodUnmappedResultPrimitiveValidateResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(float32)
-		{
-			val := res
-			goaAttributes := strconv.FormatFloat(float64(val), 'f', -1, 32)
-			w.Header().Set("Goa-Attribute", goaAttributes)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultPrimitiveWithHeadersEncodeCode = `// EncodeMethodUnmappedResultPrimitiveWithHeadersResponse returns an
-// encoder for responses returned by the
-// ServiceUnmappedResultPrimitiveWithHeaders
-// MethodUnmappedResultPrimitiveWithHeaders endpoint.
-func EncodeMethodUnmappedResultPrimitiveWithHeadersResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.(interface{})
-		if res != nil {
-			val := res
-			locations := fmt.Sprintf("%v", val)
-			w.Header().Set("Location", locations)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultArrayEncodeCode = `// EncodeMethodUnmappedResultArrayResponse returns an encoder for responses
-// returned by the ServiceUnmappedResultArray MethodUnmappedResultArray
-// endpoint.
-func EncodeMethodUnmappedResultArrayResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.([]uint)
-		if res != nil {
-			val := res
-			goaAttributesSlice := make([]string, len(val))
-			for i, e := range val {
-				es := strconv.FormatUint(uint64(e), 10)
-				goaAttributesSlice[i] = es
-			}
-			goaAttributes := strings.Join(goaAttributesSlice, ", ")
-			w.Header().Set("Goa-Attribute", goaAttributes)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultArrayValidateEncodeCode = `// EncodeMethodUnmappedResultArrayValidateResponse returns an encoder for
-// responses returned by the ServiceUnmappedResultArrayValidate
-// MethodUnmappedResultArrayValidate endpoint.
-func EncodeMethodUnmappedResultArrayValidateResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.([]uint)
-		if res != nil {
-			val := res
-			goaAttributesSlice := make([]string, len(val))
-			for i, e := range val {
-				es := strconv.FormatUint(uint64(e), 10)
-				goaAttributesSlice[i] = es
-			}
-			goaAttributes := strings.Join(goaAttributesSlice, ", ")
-			w.Header().Set("Goa-Attribute", goaAttributes)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`
-
-var UnmappedResultArrayWithHeadersEncodeCode = `// EncodeMethodUnmappedResultArrayWithHeadersResponse returns an encoder
-// for responses returned by the ServiceUnmappedResultArrayWithHeaders
-// MethodUnmappedResultArrayWithHeaders endpoint.
-func EncodeMethodUnmappedResultArrayWithHeadersResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
-	return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
-		res, _ := v.([]string)
-		if res != nil {
-			val := res
-			locations := strings.Join(val, ", ")
-			w.Header().Set("Location", locations)
-		}
-		w.WriteHeader(http.StatusOK)
-		return nil
-	}
-}
-`