1

I wrote a simple REST API (just learning) using Go and I am calling it using python requests. I have two methods, "update_x", which updates the value of the resource, "get_x_times_n" which gets the value of the resource. Logically, I would think that I need (and can only use) put for "update_x" and get for "get_x_times_n". However, I can very easily implement a method, such that I can call "get_x_times_n" using put.

Here is my Go code:

package main

import (
    "fmt"
    "log"
    "io"
    "io/ioutil"
    "net/http"
    "encoding/json"
    "strconv"
)

func main() {
    my_resource := MyResource{x: 0}

    http.HandleFunc("/update_x", my_resource.update_x)
    http.HandleFunc("/get_x_times_n", my_resource.get_x_times_n)
    http.HandleFunc("/get_x_times_n_wierd", my_resource.get_x_times_n_wierd)
    http.ListenAndServe("localhost:8000", nil)
}

type MyResource struct {
    x int 
}

func (mr *MyResource) update_x(w http.ResponseWriter, r *http.Request) {

    body, err := ioutil.ReadAll(io.Reader(r.Body))
    if err != nil {
        panic(err)
    }

    if err := r.Body.Close(); err != nil {
        panic(err)
    }

    var delta int 

    err = json.Unmarshal(body, &delta)

    if err != nil {
        panic(err)
    }
    mr.x += delta 
}

func (mr *MyResource) get_x_times_n(w http.ResponseWriter, r *http.Request) {

    err := r.ParseForm()
    if err != nil {
        panic(err)
    }

    log.Println(r.Form)

    n_str := r.Form["n"][0]

    n, err := strconv.Atoi(n_str)

    if err != nil {
        panic(err)
    }

    result := mr.x * n 

    fmt.Fprint(w, result)
}


func (mr *MyResource) get_x_times_n_wierd(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.Body)
    body, err := ioutil.ReadAll(io.Reader(r.Body))
    if err != nil {
        panic(err)
    }

    if err := r.Body.Close(); err != nil {
        panic(err)
    }

    var n int 

    err = json.Unmarshal(body, &n)

    if err != nil {
        panic(err)
    }

    result := mr.x * n 

    fmt.Fprint(w, result)
}

And here is my Python session:

In [51]: import requests

In [52]: r = requests.put("http://localhost:8000/update_x", data='5')
In [53]: r = requests.get("http://localhost:8000/get_x_times_n", params={"n": 5})

In [54]: r.content
Out[54]: '25'

In [55]: r = requests.put("http://localhost:8000/get_x_times_n_wierd", data="5")

In [56]: r.content
Out[56]: '25'

I can see why this is happening, since in both methods I have r *http.Request, I can pass whatever data I want. And also in both methods I have access to w http.ResponseWriter, I can write anything I want.

My question is: What am I doing wrong? Am I just abusing requests when using the API? Did I set up my API completely wrong? (If the API was set up properly something like this would not be allowed?). Or both? Or maybe something else?

2

Nothing builtin into the server will figure out that a request should have been a PUT request. (How could it?) It's up to you to handle that. The default behavior for the go http server is simply to ignore the method. (Some other systems have other default, such as only supporting GET by default). If you want a particular request to only work when used with a particular http method, you have to enforce it. Something like:

if r.Method != "PUT" {
   sendRejection(w);
}

My recommendation is to always enforce that the correct method is being used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.