I am developing a client-server application using RESTful web services.
I want to ask for user input on the client and send it to the server and use that name in the rest of my program but I cannot send the name to the server properly.
Below is a part of my program:
Client:
func main() {
//getting input
fmt.Println("Please enter your name: ")
reader := bufio.NewReader(os.Stdin)
myName, _ := reader.ReadString('\n')
client := &http.Client{
CheckRedirect: nil,
}
reply, err := http.NewRequest("GET", "http://localhost:8080/", nil)
reply.Header.Add("username", myName)
client.Do(reply)
if err != nil {
fmt.Println(err)
}
Server:
func CreateClient(w http.ResponseWriter, r *http.Request) {
clientName := r.Header.Get("username")
fmt.Println(clientName, "---------")//it's empty
cli := &Client{
currentRoom: nil, //starts as nil because the user is not initally in a room
outputChannel: make(chan string),
name: clientName,
}
Members = append(Members, cli)
reply := cli.name
fmt.Fprintf(w, reply)
}
on the client side, reply (reply.Header.Add("username", myName)) has the user name in the header but on the server side clientName (clientName := r.Header.Get("username")) is empty so the rest of my program won't run.
My problem is that I cannot send the user input to the server and take it back on the client side.
Can someone tell me how I can solve the problem?
You may want to have a look at http query string. Wikipedia
Sending the username.
req := http.NewRequest("GET","localhost:8080",nil)
q:=req.URL.Query()
q.Add("username",myName)
req.URL.RawQuery = q.Encode()
Resp,err:=http. Client{}.Do(req)
Recieving the username:
clientName := r.URL.Query()["username"][0]
Note that if your are going to do the same with password or any other sensitive data, please do some research on how to make it secure and DO NOT copy this code.
Related
I am creating an HTTP REST server in golang using gin-gonic
My code is:
func main() {
var port string
if len(os.Args) == 1 {
port = "8080"
} else {
port = os.Args[1]
}
router := gin.Default()
router.GET("/:a/*b", func(c *gin.Context) {
// My custom code to get "download" reader from some third party cloud storage.
// Create the file at the server side
out, err := os.Create(b)
if err != nil {
c.String(http.StatusInternalServerError, "Error in file creation at server side\n")
return
}
c.String(http.StatusOK, "File created at server side\n")
_, err = io.Copy(out, download)
if err != nil {
c.String(http.StatusInternalServerError, "Some error occured while downloading the object\n")
return
}
// Close the file at the server side
err = out.Close()
if err != nil {
c.String(http.StatusInternalServerError, "Some error occured while closing the file at server side\n")
}
// Download the file from server side at client side
c.String(http.StatusOK, "Downloading the file at client side\n")
c.FileAttachment(objectPath, objectPath)
c.String(http.StatusOK, "\nFile downlaoded at the client side successfully\n")
c.String(http.StatusOK, "Object downloaded successfully\n")
})
// Listen and serve
router.Run(":"+port)
}
When I run the curl command at the client side command prompt, it downloaded that file on my REST server, but did not download on my client side. However, the gin-gonic godoc says that:
func (*Context) File
func (c *Context) File(filepath string)
File writes the specified file into the body stream in a efficient way.
func (*Context) FileAttachment
func (c *Context) FileAttachment(filepath, filename string)
FileAttachment writes the specified file into the body stream in an efficient way On the client side, the file will typically be downloaded with the given filename
func (*Context) FileFromFS
func (c *Context) FileFromFS(filepath string, fs http.FileSystem)
FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.
But, on close observation from my command prompt output, it printed the content of the txt file, which I need to save on my client side
So, I would like to stream that download from that 3rd party storage to the client side command prompt or browser, via my custom REST API server.
Am I missing something here?
Thanks
UPDATE: I tried to write Content-Disposition & Content-Type headers to response as follows:
package main
import (
"context"
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
var port string
if len(os.Args) == 1 {
port = "8080"
} else {
port = os.Args[1]
}
router := gin.Default()
router.GET("/:a/*b", func(c *gin.Context) {
param_a := c.Param("a")
param_b := c.Param("b")
reqToken := c.GetHeader("my_custom_key")
// My custom code to get "download" reader from some third party cloud storage.
c.String(http.StatusOK, "Downloading the object\n")
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%", param_b))
c.Writer.Header().Add("Content-Type", c.GetHeader("Content-Type"))
c.File(param_b)
c.String(http.StatusOK, "Object downloaded successfully\n")
err = download.Close()
if err != nil {
c.String(http.StatusInternalServerError, "Error in closing the download of the object\n")
return
}
c.String(http.StatusOK, "Object downloaded completed & closed successfully\n")
})
// Listen and serve
router.Run(":"+port)
}
Now, it displays error, but also the success messages as follows, but the fill is still not downloaded at client side:
404 page not found
Object downloaded successfully
Object downloaded completed & closed successfully
I am trying to pass data from one golang service to another using http.NewRequest(). To do it I used following code:
httpClient := http.Client{}
userserviceUrl := "http://user:7071/checkemail"
form := url.Values{}
form.Set("uuid", uuid)
form.Set("email", email)
b := bytes.NewBufferString(form.Encode())
req, err := http.NewRequest("POST", userserviceUrl, b)
if err != nil {
log.Println(err)
}
opentracing.GlobalTracer().Inject(
validateEmailSpan.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
resp, err := httpClient.Do(req)
//_, err = http.PostForm("http://user:7071/checkemail", url.Values{"uuid": {uuid}, "email": {email}})
if err != nil {
log.Println("Couldnt verify email address user service sends an error : ", err)
}
defer resp.Body.Close()
I got this from Golang: http.NewRequest POST
When I try to dump received data from user service:
req.ParseForm()
log.Println("Form values : ", req.Form)
I get an empty map[]
Here I just try to inject tracing span to my request, previously I have used http.PostForm() to pass data, it worked perfectly. But I have no idea to pass tracing to it.
From the docs for ParseForm:
[...] when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value.
PostForm sets the Content-Type automatically, but now you have to do it yourself:
req, err := http.NewRequest("POST", userserviceUrl, strings.NewReader(form.Encode()))
// TODO: handle error
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
am working in Golang, I am building an API-Rest and am wondering, can I set cookies using restful?
I am building the methos related to the authentication of the users: login, logout,sign up, etc. and by now am trying to set a cookie in the response with the generated uuid. I have this:
func Login(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
...some code....
c := &http.Cookie{
Name: "session",
Value: uuid.NewV4().String(),
}
http.SetCookie(w, c)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(user)
w.WriteHeader(fasthttp.StatusOK)
}
But in the response I don't get any cookie, so, if is possible, how is the proper way to make it? Thank you!
You can indeed set cookies.
This would feel like it's too short of an answer though. Remember that a REST API is nothing more than a HTTP server with a very strict usage of how it should be called and what it returns. As such, you can safely set cookies.
The question is though, if that is really something you should do, have a look at JSON Web Tokens and JSON Web Encryption instead. There are Go libraries available for both. The rationale for using JWE and JWT over cookies is that you usually want a REST API to be as stateless as possible; preferring for the Client to keep state instead.
If you insist on using cookies though, consider using Gorilla's securecookie API instead, as you probably do not want people peeking into your cookie's contents. You can use it as so:
import "github.com/gorilla/securecookie"
s := securecoookie.New([]byte("very-secret-1234"), byte[]("much-hidden-5678"))
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
value := map[string]string{
"foo": "bar",
}
if encoded, err := s.Encode("cookie-name", value); err == nil {
cookie := &http.Cookie{
Name: "cookie-name",
Value: encoded,
Path: "/",
Secure: true,
HttpOnly: true,
}
http.SetCookie(w, cookie)
}
}
Similarly, you can retrieve the Cookie's contents like this:
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie("cookie-name"); err == nil {
value := make(map[string]string)
if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
fmt.Fprintf(w, "The value of foo is %q", value["foo"])
}
}
}
I need to implement web service in go that processes tar.gz files and I wonder what is the correct way, what content type I need to define, etc.
plus, I found that a lot of things are handled automatically - on the client side I just post a gzip reader as request body and Accept-Encoding: gzip header is added automatically, and on the server side - I do not need to gunzip the request body, it is already extracted to tar. does that make sense?
Can I rely that it would be like this with any client?
Server:
func main() {
router := mux.NewRouter().StrictSlash(true)
router.Handle("/results", dataupload.NewUploadHandler()).Methods("POST")
log.Fatal(http.ListenAndServe(*address, router))
}
Uploader:
package dataupload
import (
"errors"
log "github.com/Sirupsen/logrus"
"io"
"net/http"
)
// UploadHandler responds to /results http request, which is the result-service rest API for uploading results
type UploadHandler struct {
uploader Uploader
}
// NewUploadHandler creates UploadHandler instance
func NewUploadHandler() *UploadHandler {
return &UploadHandler{
uploader: TarUploader{},
}
}
func (uh UploadHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
retStatus := http.StatusOK
body, err := getBody(request)
if err != nil {
retStatus = http.StatusBadRequest
log.Error("Error fetching request body. ", err)
} else {
_, err := uh.uploader.Upload(body)
}
writer.WriteHeader(retStatus)
}
func getBody(request *http.Request) (io.ReadCloser, error) {
requestBody := request.Body
if requestBody == nil {
return nil, errors.New("Empty request body")
}
var err error
// this part is commented out since somehow the body is already gunzipped - no need to extract it.
/*if strings.Contains(request.Header.Get("Accept-Encoding"), "gzip") {
requestBody, err = gzip.NewReader(requestBody)
}*/
return requestBody, err
}
Client
func main() {
f, err := os.Open("test.tar.gz")
if err != nil {
log.Fatalf("error openning file %s", err)
}
defer f.Close()
client := new(http.Client)
reader, err := gzip.NewReader(f)
if err != nil {
log.Fatalf("error gzip file %s", err)
}
request, err := http.NewRequest("POST", "http://localhost:8080/results", reader)
_, err = client.Do(request)
if err != nil {
log.Fatalf("error uploading file %s", err)
}
}
The code you've written for the client is just sending the tarfile directly because of this code:
reader, err := gzip.NewReader(f)
...
request, err := http.NewRequest("POST", "http://localhost:8080/results", reader)
If you sent the .tar.gz file content directly, then you would need to gunzip it on the server. E.g.:
request, err := http.NewRequest(..., f)
I think that's closer to the behavior you should expect third-party clients to exhibit.
Claerly not, but maybe...
Golang provides a very good support for the http client (and server). This is one of the first language to support http2 and the design of the API clearly shows their concern on having a fast http.
This is why they add Accept-Econding: gzip automatically. That will dramatically reduce the size of the server response and then optimize the transfer.
But the gzip remains an option in http 1 and not all of the client will push this header to your server.
Note that the Content-Type describes the type of data you are sending (here a tar.gz but could be application/json, test/javascript, ...), when the Accept-Encoding describes the way the data has been encoded for the transport
Go will take care of transparently handling the Accept-Encoding for you because it is responsible of the transport of the data. Then it will be up to you to handle the Content-Type because only you know how to give a sense to the content you received
Oke, I'm currently trying to login in to my school website, with my own Crawler. Altough they have some protection against login. First I do a Get request to the Website so I get the token from the hidden Input field. That token I use in my next Post request to login to the url! But for some reason the http response is that I cannot resubmit the form. But with doing the same in Postman rest client (chrome plugin) I can login!
When I try to submit a form to this url:
postLoginUrl = "?username=%s&password=%s&submit=inloggen&_eventId=submit&credentialsType=ldap<=%s"
loginUrl = "https://login.hro.nl/v1/login"
where %s are filled in credentials
req, err := client.Post(loginUrl, "application/x-www-form-urlencoded", strings.NewReader(uri))
I'm getting as response that the Form cannot be resubmitted.
But when I try it with Postman rest client, I'm allowed to login.
code for Csrf token:
func getCSRFtoken() (key string) {
doc, err := goquery.NewDocument(loginUrl)
if err != nil {
log.Fatal(err)
}
types := doc.Find("input")
for node := range types.Nodes {
singlething := types.Eq(node)
hidden_input, _ := singlething.Attr("type")
if hidden_input == "hidden" {
key, _ := singlething.Attr("value")
return key
}
}
return ""
}
goquery.NewDocument is a http.Get()
My question now is, how does the URL get's formatted from the library
Maybe you would be better off using:
(c *Client)PostForm(url string, data url.Values) (resp *Response, err error)
from net/http like http://play.golang.org/p/8D6XI6arkz
With the params in url.Values (instead of concatenating the strings, like you are doing now.)