I have written a small piece of code in go
func loginHandler(w http.ResponseWriter, r *http.Request) {
log.Println("loginHandler")
log.Println("request url is", r.RequestURI)
log.Println("request method", r.Method)
requestbody, _ := ioutil.ReadAll(r.Body)
log.Println("request body is", string(requestbody))
if r.Method == "POST" {
us, err := globalSessions.SessionStart(w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
us.Set("LoggedInUserID", "000000")
w.Header().Set("Location", "/auth")
w.WriteHeader(http.StatusFound)
return
}
outputHTML(w, r, "static/login.html")
}
func outputHTML(w http.ResponseWriter, req *http.Request, filename string) {
log.Println("outputHTML")
requestbody, _ := ioutil.ReadAll(req.Body)
log.Println("request body is", string(requestbody))
log.Println("request body is", requestbody)
file, err := os.Open(filename)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer file.Close()
fi, _ := file.Stat()
http.ServeContent(w, req, file.Name(), fi.ModTime(), file)
}
in this code i am redirecting to login.html . now i want to send a variable let it be some string called testvariable and its value to login.html.
To be able to display values in your html you can use Go's html/template package.
First you'll need to specify where in the html page you want your values to appear, using the html/template package you can do that with template actions.
"Actions"--data evaluations or control structures--are delimited by
"{{" and "}}"
Next you'll need to drop the http.ServeContent function as that does not know how to render templates, instead you can use Execute to display the login page together with your values.
Here's an example:
login.html:
<html>
<body>
<h1>{{.MyVar}}</h1>
</body>
</html>
outputHTML:
func outputHTML(w http.ResponseWriter, filename string, data interface{}) {
t, err := template.ParseFiles(filename)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
if err := t.Execute(w, data); err != nil {
http.Error(w, err.Error(), 500)
return
}
}
And your loginHandler:
func loginHandler(w http.ResponseWriter, r *http.Request) {
// do whatever you need to do
myvar := map[string]interface{}{"MyVar": "Foo Bar Baz"}
outputHTML(w, "static/login.html", myvar)
}
Read more on templates here: html/template and for information about how to program the templates themselves, see the documentation for text/template
Related
I want to set up a web server to perform a POST request. How does the post request get executed with the code below since only HandleFunc and ListenAndServe are defined in main function?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
func post(w http.ResponseWriter, r *http.Request) {
const myurl string = "http://localhost:8000/"
request := strings.NewReader(`
{
"Name":"Tom",
"Age":"20"
}
`)
response, err := http.Post(myurl, "application/json", request)
content, err := ioutil.ReadAll(response.Body)
if err != nil {
panic(err)
}
fmt.Println(string(content))
defer response.Body.Close()
}
func main() {
http.HandleFunc("/", post)
log.Fatal(http.ListenAndServe(":8000", nil))
}
Here is a basic example of how you could go about it. I am using the same program to run both, the server and the client. This is just for demonstration purposes. You can of course make them separate programs.
// use struct to represent the data
// to recieve and send
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// run the example
func main() {
// start the server in a goroutine
go server()
// wait 1 second to give the server time to start
time.Sleep(time.Second)
// make a post request
if err := client(); err != nil {
fmt.Println(err)
}
}
// basic web server to receive a request and
// decode the body into a user struct
func server() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
user := &Person{}
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Println("got user:", user)
w.WriteHeader(http.StatusCreated)
})
if err := http.ListenAndServe(":8080", nil); err != http.ErrServerClosed {
panic(err)
}
}
// a simple client that posts a user to the server
func client() error {
user := &Person{
Name: "John",
Age: 30,
}
b := new(bytes.Buffer)
err := json.NewEncoder(b).Encode(user)
if err != nil {
return err
}
resp, err := http.Post("http://localhost:8080/", "application/json", b)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Println(resp.Status)
return nil
}
Here is the working example: https://go.dev/play/p/34GT04jy_uA
I inherited someone else's code for an API and since I'm not familiar with the requests that it's receiving I'm trying to print them or log them so I can see their structure. From what I've read about Go, jsons are decoded with Structs but since I don't know how the requests are received I cant write a struct.
I've tried the following on a basic API but they just print me out an empty map or nothing at all:
func createBook(w http.ResponseWriter, r *http.Request) {
var result map[string]interface{}
_ = json.NewDecoder(r.Body).Decode(&result)
fmt.Println(result)
func createBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var book Book
_ = json.NewDecoder(r.Body).Decode(&book)
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
newStr := buf.String()
reader := strings.NewReader(newStr)
writter := os.Stdout
dec := json.NewDecoder(reader)
enc := json.NewEncoder(writter)
for {
var m map[string]interface{}
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
if err := enc.Encode(&m); err != nil {
log.Println(err)
}
fmt.Println(m)
}
book.ID = strconv.Itoa(rand.Intn(100000000)) // Mock ID - not safe
books = append(books, book)
json.NewEncoder(w).Encode(book)
}
Is there any other way that it would print the received json without me knowing the strut beforehand?
Use json.Unmarshal function
import "bytes"
func createBook(w http.ResponseWriter, r *http.Request) {
var result map[string]interface{}
data :+ StreamToByte(r.Body)
err := json.Unmarshal(data, &result)
if err !=nil{
fmt.Println(err) //better to use log
}else
fmt.Println(result)
}
}
func StreamToByte(stream io.Reader) []byte {
buf := new(bytes.Buffer)
buf.ReadFrom(stream)
return buf.Bytes()
}
Refer :
https://appdividend.com/2020/02/28/golang-how-to-convert-json-to-map-in-go/
https://gist.github.com/dixudx/3989284b142414e10352fde9def5c771
I am wondering why saving to json file does'nt work as I expected.
-If I input values in the fields and click submit button
-The form will submit and the process function executes
-The process.html renders the input values.
-The input values not saving to the json file.
import (
"net/http"
"html/template"
"os"
"encoding/json"
)
var tpl *template.Template
type Data struct {
First string `json:"First"`
Last string `json:"Last"`
}
func init() {
tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}
func main() {
http.HandleFunc("/", index);
http.HandleFunc("/process", process);
http.ListenAndServe(":80", nil);
}
func index(w http.ResponseWriter, r *http.Request) {
tpl.ExecuteTemplate(w, "index.gohtml", nil)
}
func process(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
f, err := os.Open("name.json");
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer f.Close();
data := new(Data)
data.First = r.FormValue("first");
data.Last = r.FormValue("last");
b, err := json.Marshal(data)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
f.Write(b)
f.Close()
tpl.ExecuteTemplate(w, "process.gohtml", data)
}
I believe that os.Open defaults to read-only. I think you want something like os.OpenFile.
Assume a Go program with several handler functions like this:
type FooRequest struct {
FooField string `json:"foofield"`
// ...
}
type FooResponse struct {
BarField string `json:"barfield"`
// ...
}
func handleFoo(w http.ResponseWriter, r *http.Request) {
var req FooRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// do what actually matters:
foo := DoStuff(req)
baz, err := DoSomething(foo)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resp := DoEvenMoreStuff(baz)
// back to boiler plate:
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
How could this code be refactored to avoid the JSON decoding/encoding boiler plate?
I would probably like to see a generic "handle JSON" func and another func handling the actual foo stuff:
func handleJson(w http.ResponseWriter, r *http.Request) {
var req FooRequest // what about this line?
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resp, err := handleFooElegantly(req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func handleFoo(req FooRequest) (FooResponse, error) {
var resp FooResponse
foo := DoStuff(req)
baz, err := DoSomething(foo)
if err != nil {
return resp, err
}
resp = DoEvenMoreStuff(baz)
return resp, nil
}
That leaves us with the problem of telling the JSON decoder the type it should try to decode.
What would be the idiomatic Go way of implementing this?
You can use reflection to eliminate the boilerplate. Here's an example that adapts a func(pointer) (pointer, error) to an http.Handler that handles the JSON encoding and decoding.
type jsonHandler struct {
fn interface{}
}
func (jh jsonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// allocate the input argument
v := reflect.ValueOf(jh.fn)
arg := reflect.New(v.Type().In(0).Elem())
// decode to the argument
if err := json.NewDecoder(r.Body).Decode(arg.Interface()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// invoke the function
out := v.Call([]reflect.Value{arg})
// check error
if err, ok := out[1].Interface().(error); ok && err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// decode result
if err := json.NewEncoder(w).Encode(out[0].Interface()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
Here's an example of how to use this adapter:
type Input struct {
A, B int
}
type Output struct {
Result int
}
func add(in *Input) (*Output, error) {
return &Output{Result: in.A + in.B}, nil
}
handler := jsonHandler{add} // handler satisfies http.Handler
working playground example
The code will panic if the function does not have a single pointer argument and return a pointer and an error. A more robust and complete implementation should check that the function meets these constraints before returning the handler.
func newHandler(fn interface{)) (http.Handler, error) {
// use reflect to check fn, return error
...
return jsonHandler{fn}, nil
}
The use of reflection in this answer is somewhat similar to an approach used in the standard library.
Question: Currently I'm printing out my response in the func Index
like this fmt.Fprintf(w, string(response)) however, how can I send JSON properly in the request so that it maybe consumed by a view?
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
"log"
"encoding/json"
)
type Payload struct {
Stuff Data
}
type Data struct {
Fruit Fruits
Veggies Vegetables
}
type Fruits map[string]int
type Vegetables map[string]int
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
response, err := getJsonResponse();
if err != nil {
panic(err)
}
fmt.Fprintf(w, string(response))
}
func main() {
router := httprouter.New()
router.GET("/", Index)
log.Fatal(http.ListenAndServe(":8080", router))
}
func getJsonResponse()([]byte, error) {
fruits := make(map[string]int)
fruits["Apples"] = 25
fruits["Oranges"] = 10
vegetables := make(map[string]int)
vegetables["Carrats"] = 10
vegetables["Beets"] = 0
d := Data{fruits, vegetables}
p := Payload{d}
return json.MarshalIndent(p, "", " ")
}
You can set your content-type header so clients know to expect json
w.Header().Set("Content-Type", "application/json")
Another way to marshal a struct to json is to build an encoder using the http.ResponseWriter
// get a payload p := Payload{d}
json.NewEncoder(w).Encode(p)
Other users were commenting that the Content-Type is plain/text when encoding.
You have to set the content type with w.Header().Set() first, then write the HTTP response code with w.WriteHeader().
If you call w.WriteHeader() first, then call w.Header().Set() after you will get plain/text.
An example handler might look like this:
func SomeHandler(w http.ResponseWriter, r *http.Request) {
data := SomeStruct{}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(data)
}
You can do something like this in you getJsonResponse function -
jData, err := json.Marshal(Data)
if err != nil {
// handle error
}
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
In gobuffalo.io framework I got it to work like this:
// say we are in some resource Show action
// some code is omitted
user := &models.User{}
if c.Request().Header.Get("Content-type") == "application/json" {
return c.Render(200, r.JSON(user))
} else {
// Make user available inside the html template
c.Set("user", user)
return c.Render(200, r.HTML("users/show.html"))
}
and then when I want to get JSON response for that resource I have to set "Content-type" to "application/json" and it works.
I think Rails has more convenient way to handle multiple response types, I didn't see the same in gobuffalo so far.
You may use this package renderer, I have written to solve this kind of problem, it's a wrapper to serve JSON, JSONP, XML, HTML etc.
This is a complement answer with a proper example:
func (ch captureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("error reading request body, %v", err), http.StatusInternalServerError)
return
}
...do your stuff here...
case http.MethodGet:
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode( ...put your object here...)
if err != nil {
http.Error(w, fmt.Sprintf("error building the response, %v", err), http.StatusInternalServerError)
return
}
default:
http.Error(w, fmt.Sprintf("method %s is not allowed", r.Method), http.StatusMethodNotAllowed)
}
}