feat: standardize transport contracts across Gin and nethttp
This commit is contained in:
@@ -20,7 +20,9 @@ type Middleware func(Handler) Handler
|
||||
// Router is the small contract the application needs from a network layer.
|
||||
// The default implementation is backed by Gin, but another router or a
|
||||
// future GoOmni transport can implement this interface without changing
|
||||
// business handlers.
|
||||
// business handlers. Register routes before serving requests. Implementations
|
||||
// should pass transport/transporttest.Run; see transport/README.md for the
|
||||
// portable path, method, fallback and context-propagation rules.
|
||||
type Router interface {
|
||||
http.Handler
|
||||
Handle(method, path string, handler http.Handler)
|
||||
@@ -162,16 +164,25 @@ func (c *Context) BindURI(value any) error { return validate.URI(c.params, value
|
||||
|
||||
// JSON is an escape hatch for responses that do not use the standard envelope.
|
||||
func (c *Context) JSON(status int, value any) error {
|
||||
if c.responded {
|
||||
return nil
|
||||
}
|
||||
if c.writer == nil {
|
||||
return apierrors.Internal(nil)
|
||||
}
|
||||
c.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
c.writer.WriteHeader(status)
|
||||
if err := json.NewEncoder(c.writer).Encode(value); err != nil {
|
||||
// Encode before committing headers so an invalid value can still produce
|
||||
// the standard 500 response on every adapter.
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return apierrors.Internal(err)
|
||||
}
|
||||
c.responded = true
|
||||
c.status = status
|
||||
c.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
c.writer.WriteHeader(status)
|
||||
if _, err := c.writer.Write(append(data, '\n')); err != nil {
|
||||
return apierrors.Internal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -186,9 +197,15 @@ func (c *Context) Accepted(data any) error {
|
||||
}
|
||||
|
||||
func (c *Context) NoContent() error {
|
||||
c.writer.WriteHeader(http.StatusNoContent)
|
||||
if c.responded {
|
||||
return nil
|
||||
}
|
||||
if c.writer == nil {
|
||||
return apierrors.Internal(nil)
|
||||
}
|
||||
c.responded = true
|
||||
c.status = http.StatusNoContent
|
||||
c.writer.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -213,7 +230,8 @@ func (c *Context) Status() int {
|
||||
func (c *Context) Responded() bool { return c.responded }
|
||||
|
||||
// WriteError writes a stable public error response. Internal causes are not
|
||||
// serialized and a partially written response is preserved.
|
||||
// serialized. Responses committed through Context methods are preserved;
|
||||
// direct Raw writes are outside this response-tracking contract.
|
||||
func (c *Context) WriteError(err error) {
|
||||
if c.responded {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user