Skip to content

Commit

Permalink
add content negotiation feature, add context.ReadYAML and fix kataras…
Browse files Browse the repository at this point in the history
  • Loading branch information
kataras committed Aug 3, 2019
1 parent 2a6284b commit 8ee0de5
Show file tree
Hide file tree
Showing 15 changed files with 985 additions and 133 deletions.
2 changes: 2 additions & 0 deletions _examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
- [Read JSON](http_request/read-json/main.go)
* [Struct Validation](http_request/read-json-struct-validation/main.go)
- [Read XML](http_request/read-xml/main.go)
- [Read YAML](http_request/read-yaml/main.go) **NEW**
- [Read Form](http_request/read-form/main.go)
- [Read Query](http_request/read-query/main.go) **NEW**
- [Read Custom per type](http_request/read-custom-per-type/main.go)
Expand All @@ -237,6 +238,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
### How to Write to `context.ResponseWriter() http.ResponseWriter`

- [Content Negotiation](http_responsewriter/content-negotiation) **NEW**
- [Write `valyala/quicktemplate` templates](http_responsewriter/quicktemplate)
- [Write `shiyanhui/hero` templates](http_responsewriter/herotemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
Expand Down
2 changes: 2 additions & 0 deletions _examples/README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her

- [读取JSON](http_request/read-json/main.go)
- [读取XML](http_request/read-xml/main.go)
- [读取YAML](http_request/read-yaml/main.go) **更新**
- [读取Form](http_request/read-form/main.go)
- [读取Query](http_request/read-query/main.go) **更新**
- [读取每个类型的自定义结果Custom per type](http_request/read-custom-per-type/main.go)
Expand All @@ -342,6 +343,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her

### 如何写入`context.ResponseWriter() http.ResponseWriter`

- [Content Negotiation](http_responsewriter/content-negotiation) **更新**
- [`valyala/quicktemplate`模版](http_responsewriter/quicktemplate)
- [`shiyanhui/hero`模版](http_responsewriter/herotemplate)
- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go)
Expand Down
36 changes: 36 additions & 0 deletions _examples/http_request/read-yaml/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"github.com/kataras/iris"
)

func newApp() *iris.Application {
app := iris.New()
app.Post("/", handler)

return app
}

// simple yaml stuff, read more at https://yaml.org/start.html
type product struct {
Invoice int `yaml:"invoice"`
Tax float32 `yaml:"tax"`
Total float32 `yaml:"total"`
Comments string `yaml:"comments"`
}

func handler(ctx iris.Context) {
var p product
if err := ctx.ReadYAML(&p); err != nil {
ctx.StatusCode(iris.StatusBadRequest)
ctx.WriteString(err.Error())
return
}

ctx.Writef("Received: %#+v", p)
}

func main() {
app := newApp()
app.Run(iris.Addr(":8080"))
}
24 changes: 24 additions & 0 deletions _examples/http_request/read-yaml/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"testing"

"github.com/kataras/iris/httptest"
)

func TestReadYAML(t *testing.T) {
app := newApp()
e := httptest.New(t, app)

expectedResponse := `Received: main.product{Invoice:34843, Tax:251.42, Total:4443.52, Comments:"Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338."}`
send := `invoice: 34843
tax : 251.42
total: 4443.52
comments: >
Late afternoon is best.
Backup contact is Nancy
Billsmer @ 338-4338.`

e.POST("/").WithHeader("Content-Type", "application/x-yaml").WithBytes([]byte(send)).Expect().
Status(httptest.StatusOK).Body().Equal(expectedResponse)
}
114 changes: 114 additions & 0 deletions _examples/http_responsewriter/content-negotiation/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Package main contains three different ways to render content based on the client's accepted.
package main

import "github.com/kataras/iris"

type testdata struct {
Name string `json:"name" xml:"Name"`
Age int `json:"age" xml:"Age"`
}

func newApp() *iris.Application {
app := iris.New()
app.Logger().SetLevel("debug")

// app.Use(func(ctx iris.Context) {
// requestedMime := ctx.URLParamDefault("type", "application/json")
//
// ctx.Negotiation().Accept.Override().MIME(requestedMime, nil)
// ctx.Next()
// })

app.Get("/resource", func(ctx iris.Context) {
data := testdata{
Name: "test name",
Age: 26,
}

// Server allows response only JSON and XML. These values
// are compared with the clients mime needs. Iris comes with default mime types responses
// but you can add a custom one by the `Negotiation().Mime(mime, content)` method,
// same for the "accept".
// You can also pass a custom ContentSelector(mime string) or ContentNegotiator to the
// `Context.Negotiate` method if you want to perform more advanced things.
//
//
// By-default the client accept mime is retrieved by the "Accept" header
// Indeed you can override or update it by `Negotiation().Accept.XXX` i.e
// ctx.Negotiation().Accept.Override().XML()
//
// All these values can change inside middlewares, the `Negotiation().Override()` and `.Accept.Override()`
// can override any previously set values.
// Order matters, if the client accepts anything (*/*)
// then the first prioritized mime's response data will be rendered.
ctx.Negotiation().JSON().XML()
// Accept-Charset vs:
ctx.Negotiation().Charset("utf-8", "iso-8859-7")
// Alternatively you can define the content/data per mime type
// anywhere in the handlers chain using the optional "v" variadic
// input argument of the Context.Negotiation().JSON,XML,YAML,Binary,Text,HTML(...) and e.t.c
// example (order matters):
// ctx.Negotiation().JSON(data).XML(data).Any("content for */*")
// ctx.Negotiate(nil)

// if not nil passed in the `Context.Negotiate` method
// then it overrides any contents made by the negotitation builder above.
_, err := ctx.Negotiate(data)
if err != nil {
ctx.Writef("%v", err)
}
})

app.Get("/resource2", func(ctx iris.Context) {
jsonAndXML := testdata{
Name: "test name",
Age: 26,
}

// I prefer that one, as it gives me the freedom to modify
// response data per accepted mime content type on middlewares as well.
ctx.Negotiation().
JSON(jsonAndXML).
XML(jsonAndXML).
HTML("<h1>Test Name</h1><h2>Age 26</h2>")

ctx.Negotiate(nil)
})

app.Get("/resource3", func(ctx iris.Context) {
// If that line is missing and the requested
// mime type of content is */* or application/xml or application/json
// then 406 Not Acceptable http error code will be rendered instead.
//
// We also add the "gzip" algorithm as an option to encode
// resources on send.
ctx.Negotiation().JSON().XML().HTML().EncodingGzip()

jsonAndXML := testdata{
Name: "test name",
Age: 26,
}

// Prefer that way instead of the '/resource2' above
// if "iris.N" is a static one and can be declared
// outside of a handler.
ctx.Negotiate(iris.N{
// Text: for text/plain,
// Markdown: for text/mardown,
// Binary: for application/octet-stream,
// YAML: for application/x-yaml,
// JSONP: for application/javascript
// Other: for anything else,
JSON: jsonAndXML, // for application/json
XML: jsonAndXML, // for application/xml or text/xml
HTML: "<h1>Test Name</h1><h2>Age 26</h2>", // for text/html
})
})

return app
}

func main() {
app := newApp()
app.Run(iris.Addr(":8080"))
}
78 changes: 78 additions & 0 deletions _examples/http_responsewriter/content-negotiation/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"bytes"
"compress/gzip"
"encoding/xml"
"io/ioutil"
"testing"

"github.com/kataras/iris/httptest"
)

func TestContentNegotiation(t *testing.T) {
var (
expectedJSONResponse = testdata{
Name: "test name",
Age: 26,
}
expectedXMLResponse, _ = xml.Marshal(expectedJSONResponse)
expectedHTMLResponse = "<h1>Test Name</h1><h2>Age 26</h2>"
)

e := httptest.New(t, newApp())

e.GET("/resource").WithHeader("Accept", "application/json").
Expect().Status(httptest.StatusOK).
ContentType("application/json", "utf-8").
JSON().Equal(expectedJSONResponse)
e.GET("/resource").WithHeader("Accept", "application/xml").WithHeader("Accept-Charset", "iso-8859-7").
Expect().Status(httptest.StatusOK).
ContentType("application/xml", "iso-8859-7").
Body().Equal(string(expectedXMLResponse))

e.GET("/resource2").WithHeader("Accept", "application/json").
Expect().Status(httptest.StatusOK).
ContentType("application/json", "utf-8").
JSON().Equal(expectedJSONResponse)
e.GET("/resource2").WithHeader("Accept", "application/xml").
Expect().Status(httptest.StatusOK).
ContentType("application/xml", "utf-8").
Body().Equal(string(expectedXMLResponse))
e.GET("/resource2").WithHeader("Accept", "text/html").
Expect().Status(httptest.StatusOK).
ContentType("text/html", "utf-8").
Body().Equal(expectedHTMLResponse)

e.GET("/resource3").WithHeader("Accept", "application/json").
Expect().Status(httptest.StatusOK).
ContentType("application/json", "utf-8").
JSON().Equal(expectedJSONResponse)
e.GET("/resource3").WithHeader("Accept", "application/xml").
Expect().Status(httptest.StatusOK).
ContentType("application/xml", "utf-8").
Body().Equal(string(expectedXMLResponse))

// test html with "gzip" encoding algorithm.
rawGzipResponse := e.GET("/resource3").WithHeader("Accept", "text/html").
WithHeader("Accept-Encoding", "gzip").
Expect().Status(httptest.StatusOK).
ContentType("text/html", "utf-8").
ContentEncoding("gzip").
Body().Raw()

zr, err := gzip.NewReader(bytes.NewReader([]byte(rawGzipResponse)))
if err != nil {
t.Fatal(err)
}

rawResponse, err := ioutil.ReadAll(zr)
if err != nil {
t.Fatal(err)
}

if expected, got := expectedHTMLResponse, string(rawResponse); expected != got {
t.Fatalf("expected response to be:\n%s but got:\n%s", expected, got)
}

}
6 changes: 6 additions & 0 deletions _examples/websocket/basic/browser/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
};
}

const username = window.prompt("Your username?");

async function runExample() {
// You can omit the "default" and simply define only Events, the namespace will be an empty string"",
// however if you decide to make any changes on this example make sure the changes are reflecting inside the ../server.go file as well.
Expand All @@ -70,6 +72,10 @@
addMessage(msg.Body);
}
}
},{
headers: {
"X-Username": username,
}
});

// You can either wait to conenct or just conn.connect("connect")
Expand Down
5 changes: 4 additions & 1 deletion _examples/websocket/basic/go-client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ func main() {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(dialAndConnectTimeout))
defer cancel()

client, err := websocket.Dial(ctx, websocket.DefaultGorillaDialer, endpoint, clientEvents)
// username := "my_username"
// dialer := websocket.GobwasDialer(websocket.GobwasDialerOptions{Header: websocket.GobwasHeader{"X-Username": []string{username}}})
dialer := websocket.DefaultGobwasDialer
client, err := websocket.Dial(ctx, dialer, endpoint, clientEvents)
if err != nil {
panic(err)
}
Expand Down
11 changes: 10 additions & 1 deletion _examples/websocket/basic/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ func main() {
SigningMethod: jwt.SigningMethodHS256,
})

idGen := func(ctx iris.Context) string {
if username := ctx.GetHeader("X-Username"); username != "" {
return username
}

return websocket.DefaultIDGenerator(ctx)
}

// serves the endpoint of ws://localhost:8080/echo
websocketRoute := app.Get("/echo", websocket.Handler(websocketServer))
// with optional custom ID generator.
websocketRoute := app.Get("/echo", websocket.Handler(websocketServer, idGen))

if enableJWT {
// Register the jwt middleware (on handshake):
Expand Down
Loading

0 comments on commit 8ee0de5

Please sign in to comment.