当前位置:网站首页>Golang Foundation (7)

Golang Foundation (7)

2022-06-12 11:20:00 It's haohaozi

Twelve 、 Stand up again : Recover from failure

Every program will encounter errors . You should plan for them . occasionally , Handling errors can be as simple as reporting an error and exiting the program . But other errors may require additional action . You may need to close open files or network connections , Or clean up in other ways , So your program doesn't leave a mess .
We know that C++ The memory in the heap area is manually allocated and released by the programmer . If the program crashes before freeing memory , Then it is very likely that the memory is not released , There are many such cases , Will continue to consume memory , This affects the performance of the operating system .
Can pass defer Statement to deal with this problem . You can take defer Keyword before any normal function or method call ,Go Will delay ( That is, postpone ) Perform function calls , Until the current function exits .

Use deferred function calls to recover from errors

package main

import "fmt"

func Socialize() {
    
    defer fmt.Println("Goodbye!")
    fmt.Println("Hello!")
    fmt.Println("Nice weather,eh?")
}

func main() {
    
    Socialize() 
}
//  The first function call is deferred to Socialize after 
// Hello!
// Nice weather, eh?
// Goodbye!

“defer” Keywords by using return Keyword to ensure that a function call occurs , Even if the calling function exits early .

Use deferred function calls to ensure that the file is closed

because defer Keywords can ensure “ in any case ” All execute function calls , So it is usually used for code that needs to be run , Even in the case of errors . A common example is to close files after they are opened .

func OpenEile(fileName string) (*os.File, error) {
    
    fmt.Println("Opening", fileName)
    return os.Open(fileName)
}
func CloseFile(file *os.File) {
    
    fmt.Println("Closing file")
    file.Close()
}

func GetFloats(fileName string) ([]float64, error) {
    
    var numbers []float64
    file,err:=OpenFile(fileName)
    if err!=nil{
    
        return nil,err
    }
    
    //  Move this to just called OpenFile after 
    //  add to “defer”, In this way GetFlows It will also run after exiting 
    defer CloseFile(file)
    scanner:=bufio.NewScanner(file)
    for scanner.Scan() {
    
        number,err:=strconv.ParseFloat(scanner.Text(),64)
        if err!=nil {
    
        //  Now? , Even if an error is returned here ,CloseFile Will still be called !
        return nill, err //  Now even if an error is returned here ,CloseFile It will still be called !
    }
    numbers=append(numbers,number)
}
//  If an error is returned here ,CloseFile It will still be called !
    if scanner.Err()!=nil{
    
        return nil, scanner.Err()
    }
    return nunbers,ni1 //  Of course, if GetFToats Normal completion ,  Will call CloseFile
}

List the files in the directory

Try to create a new one called my_directory The catalog of , It contains two files and a subdirectory , As shown on the right . The following procedure will list my_directory The content of , Indicates the name of each item it contains , And whether it is a file or a subdirectory .
Try to create a new one called my_directory The catalog of , It contains two files and a subdirectory , As shown on the right . The following procedure will list my_directory The content of , Indicates the name of each item it contains , And whether it is a file or a subdirectory .

import (
    "fmt"
    "io/ioutill"
    "log"
)

func main() {
    
    file,err:=ioutill.ReadDir("my_directory")
    if err != nill {
    
        log.Fatal(err)
    }
    
    for _, file := range files {
    
        if file.IsDir() {
    
            fmt.Println("Directory:", file.Name())
        } else {
    
            fmt.Println("File:", file.Name())
        }
    }
}

List files in subdirectories ( recursive )

package main

import (
    "fmt"
    "io/ioutill"
    "log"
    "path/filepath"
)

//  Recursive function , It accepts the path to be scanned , Returns any errors encountered 
func scanDirectory(path string) error {
    
    fmt.Println(path) //  Print current directory 
    //  Get slice containing content 
    file, err := ioutill.ReadDir(path)
    if err != nil {
    
        return err
    }
    for _, file := range files {
    
        filePath := filepath.Join(path, file.Name())
        if file.IsDir() {
    
            err := scanDirectory(filePath)
            if err != nil {
    
                 return err
            }
        } else {
    
            fmt.Println(filePath)
        }
    }
    return ni;
}
func main() {
    
    err := scanDirectory("go")
    if err != nil {
    
        log.Fatal(err)
    }
}

Error handling in recursive functions

To launch a panic

When the program appears panic when , The current function stops running , The program prints log messages and crashes . You can simply call the built-in panic Function to trigger panic.

Deferred calls complete before crash

When the program appears panic when , All deferred function calls will still be executed . If there are multiple deferred calls , They will be executed in the reverse order of the delay .

func main() {
    
    one()
}
func one() {
    
    defer fmt.Println("deferred in one()")
    two()
}
func two() {
    
    defer fmt.Println("deferred in two()")
    painc("Let's see what's been deferred!")
}
// deferred in two()
// deferred in one()
// panic: Let's see what's been deferred!
package main

import (
    "fmt"
    "io/ioutill"
    "log"
    "path/filepath"
)

//  Recursive function , It accepts the path to be scanned , Returns any errors encountered 
func scanDirectory(path string) {
    
    fmt.Println(path) //  Print current directory 
    //  Get slice containing content 
    file, err := ioutill.ReadDir(path)
    if err != nil {
    
        panic(err) //  No error value is returned , But pass it on to panic
    }
    for _, file := range files {
    
        filePath := filepath.Join(path, file.Name())
        if file.IsDir() {
    
             scanDirectory(filePath)
        } else {
    
            fmt.Println(filePath)
        }
    }
}

func main() {
    
    scanDirectory("go") //  No longer need to store or check error return values 
}

Now? , When scanDirectory Error encountered while reading directory , It produces panic. all scanDirectory All recursive calls to exit .
Inaccessible files 、 Network failures and incorrect user input should generally be considered “ natural ”, It should be handled properly by error values . Usually , call panic Should be left “ impossible ” situation : An error is an error in a program , Not user errors .

recover function

Go Provides a built-in recover function , Can prevent the program from falling into panic. We need to use it to exit the program gracefully . Call during normal program execution recover when , It only returns nil, Without doing anything else ; If the program is in panic Called when the state is recover, It will stop panic. But when you call in a function panic when , This function will stop execution . therefore , stay panic Call... In the same function recover It makes no sense , because panic It will continue anyway :

func freakOut() {
    
    painc("oh no")
    recover() //  Never run 
}

however , When the program falls into panic when , There is a method that can call recover…… stay panic period , Any deferred function calls will complete . therefore , You can put a... In a separate function recover call , And is causing panic Code before using defer Call this function .

func calmDown() {
    
    recover()
}
func freakOut() {
    
    defer calmDown()
    panic("oh no")
}
func main() {
    
    freakOut()
    fmt.Println("Exiting normally")
}
//  call recover Will not result in panic Resume execution when , At least not completely recovered . produce panic The function of will immediately 
//  Return , In this function block panic Any subsequent code will not execute . however , Is producing panic After the function of returns , Normal execution will resume .
func calmDown() {
    
    recover()
}
func freakOut() {
    
    defer calmDown()
    panic("oh no") //  When it comes back ,freakOut Return at this location 
    fmt.Println("I won't be rnu!")
}
func main() {
    
    freakOut()
    fmt.Println("Exiting normally") //  This code is in freakOut Run after returning 
}

panic Value from recover Back in

Introducing panic Function time , We mentioned that the type of the parameter is interface{}, Empty interface , therefore panic Any value can be accepted . Again ,recover The type of return value of is also interface{}. You can take recover The return value of is passed to such as Println( It accepts interface{} value ) And so on. fmt function , But you can't call methods directly on it .
Here are some that will error The value passed to the panic Code for . But in doing so ,error Be converted into a interface{} value . When the deferred function is called later recover when , The return is interface{} value . therefore , Even if the bottom error The value has a Error Method , Trying to call interface{} It's worth it Error Causes a compilation error .

func calmDown() {
    
    p:=recover() //  Return to one interface{} value 
    fmt.Println(p.Error()) //  Even if the bottom “error” The value has a Error Method , but interface{} No value ,  Compile error 
}
func main() {
    
    defer calmDown()
    err:=fmt.Error("there's an error")
    panic(err) //  Pass the error value instead of the string to “panic”
}

Right panic Value to call a method or perform other operations , You need to convert it back to its underlying type using type assertions .

func calmDown() {
    
    p:=recover()
    err, ok := p.(error) //  Assertion panic The type of value is “error”
    if ok {
    
        fmt.Println(err.Error()) //  Now there's one error value , We can call Error Method 
    }
}
func main() {
    
    defer calmDown()
    err := fmt.Errorf("there's an error")
    panic(err) // there's an error
}

from scanDirectory Medium panic recovery

stay scanDirectory Function to add a pair of panic To clear the error handling code , But it also causes the program to crash . We can use what we have learned so far about defer、panic and recover All the knowledge of , To print error messages , And exit the program with dignity . We can do this by adding a reportPanic Function to achieve this , We will be in main Use in defer Call it . We're calling scanDirectory Before calling it , This may lead to potential panic.
stay reportPanic in , We call recover And store the panic value . If the program is in panic state , This will stop panic.

package main

import (
    "fmt"
    "io/ioutill"
    "path/filepath"
)
func reportPanic() {
    
    p:=recover() //  call recover And store its return value 
    //  If “”recover return nil, There is no panic
    if p==nil {
    
        return //  So do nothing 
    }
    err, ok := p.(error) //  Otherwise, get the underlying “error” value 
    if ok {
    
        fmt.Println(err) //  Then print it out 
    }
}

func scanDirectory(path string) {
    
    fmt.Println(path)
    files,err:=ioutill.ReadDir(path)
    if err != nil {
    
        panic(err)
    }
    for _, file := range files {
    
        filePath := filePath.Join(path,file.Name())
        if file.IsDir() {
    
            scanDirectory(filePath)
        } else {
    
            fmt.Println(filePath)
        }
    }
}
func main() {
    
    //  The call may cause panic Before the code , Delay invoking new reportPanic function 
    defer reportPanic()
    scanDirectory("go")
}

recovery panic

reportPanic There is also a potential problem to be solved . Now? , It can intercept any panic, Even if not from scanDirectory. If panic Value cannot be converted to error type ,reportPanic It will not be printed .
We can pass in main Use one string Parameter to add another pair panic To test :

func main() {
    
    defer reportPanic()
    panic("some other issue") //  Introduce a string with panic New values panic
    scanDirectory("go")
}

reportPanic Function from New panic To recover , But because panic Value is not a error, therefore reportPanic It won't print . Our users don't know why the program failed !
The code below has been updated reportPanic To deal with unexpected panic. If you will panic Value to error Type assertion of succeeded , We just need to print it as before . But if it fails , We just need the same panic Value is called again panic.

func reportPanic() {
    
    p:=recover()
    if p == nil {
    
        return
    }
    err,ok:=p.(error)
    if ok {
    
        fmt.Println(err)
    } else {
    
    //  If panic It's not worth it error, Then use the same value to restore panic
        panic(p)
    }
}

13、 ... and 、 Sharing work :goroutine and channel

The following procedure uses net/http The package is connected to a site , And retrieve a through several function calls Web page .
http.Response Object is a struct, Its Body Fields represent the contents of the page .Body Satisfy io Bag ReadCloser Interface , That means it has a Read Method ( Allows us to read page data ) And a Close Method ( Release the network connection when finished ).
We will delay the call to Close, This way, the connection will be released after reading . Then we pass the response body to ioutil Bag ReadAll function , This function will read its entire contents and treat it as byte Value returns .

package main

import (
    "fmt"
    "io/ioutill"
    "log"
    "net/http"
)
func main() {
    
    response,err:=http.Get("http://example.com")
    if err!=nil {
    
        log.Fatal(err)
    }
    //  once main Function exit , Release the network connection 
    defer response.Body.Close() 
    //  Read all the data in the response 
    body,err:=ioutill.ReadAll(response.Body)
    if err != nil {
    
        log.Fatal(err)
    }
    //  Convert the data to a string and print 
    fmt.Println(string(body))
}

byte type
It is Go Basic type ( Such as float64 or bool) One of , Used to save raw data , For example, you may read data from files or network connections . If you print directly byte Value slice , It doesn't show anything meaningful , But if byte Value slice converted to string, Will return readable text .

Use goroutine The concurrency of

When responseSize call http.Get when , The program must wait there for a response from the remote web site . It didn't do anything useful while waiting .
stay Go in , Concurrent tasks are called goroutine. Other programming languages have a similar concept , It's called a thread , however goroutine Requires less computer memory than threads , Less time to start and stop , This means you can run more at the same time goroutine.
To start a goroutine, have access to go sentence , It's just a normal function or method call , There is go keyword :

go myFunction()
go otherFunction("argument")

The compiler prevents you from trying to use go Get the return value from the function called by the statement .
however goroutine There is a way of communication between them :channel.channel Not only does it allow you to change the value from one goroutine Send to another goroutine, Also make sure that when you receive goroutine Before attempting to use this value , Sent goroutine The value has been sent .
Use channel The only practical way is from a goroutine To another goroutine Communication for . So to demonstrate channel, We need to do something :

  • Create a channel.
  • Write a function , This function receives a channel As a parameter . We will be in a separate goroutine Run this function in , And use it through channel Send value .
  • In the beginning goroutine Receive the value sent in .

Every channel Carry only specific types of values , So there may be a channel be used for int value , the other one channel be used for struct Type value . To declare to contain channel The variable of , have access to chan keyword , And then there was channel The type of value to be carried .
var myChannel chan float64
To actually create channel, You need to call the built-in make function ( The same functions that create maps and slices ). Pass on make To create channel The type of ( It should be of the same type as the variable to be assigned to it ).

var myChannel chan float64
myChannel = make(chan float64)

Not a separate statement channel Variable , in the majority of cases , It is easier to declare with a short variable :
myChannel :=make(chan float64)

Use channel Send and receive values

//  To be in channel Send value on , have access to <- Operator 
myChannel <- 3.14
//  Use <- Operator to receive data from channel Value 
<- myChannel
//  take channel As a parameter 
func greeting (myChannel chan string) {
    
    myChannel <- "hi" //  adopt channel Send a value 
}
func main() {
    
    //  Create a new channel
    myChannel:=make(chan string)
    //  take channel Pass on to the new goroutine Functions running in 
    go greeting(myChannel)
    //  from channel Receives the value 
    fmt.Println(<-myChannel) // hi
}

goroutine Each direction myChanne Sending a value will block , until main goroutine Until it is received .
main goroutine Become multiple goroutine The coordinator of , Only when it is ready to read the values they send , To allow them to continue .
The send operation is blocked greeting goroutine, until main goroutine Received this value .

Use channel Fix our page size program

Our program for reporting page size still has two problems :

  • We can't go Use in statement responseSize The return value of the function .
  • Before receiving the response size , our main goroutine Already completed , So I
    We added a pair time.Sleep Of 5 Second call . however 5 Seconds are sometimes too long , Sometimes it's too short .
packag main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)
//  towards responseSize Pass a channel, So that it sends the page size 
func responseSize(url string, channel chan int) {
    
    fmt.Println("Getting",url)
    response,err:=http.Get(url)
    if err!=nil {
    
        log.Fatal(err)
    }
    defer response.Body.Close()
    body,err:=ioutil.ReadAll(response.Body)
    if err!=nil {
    
        log.Fatal(err)
    }
    //  Do not return page size , But through channel send out 
    channel <- len(body)
}
fun main() {
    
    size:=make(char int)
    //  Every time you call responseSize when , All will channel Pass on the past 
    go responseSize("http://example.com/",size)
    go responseSize("http://example.com/",size)
    go responseSize("http://example.com/",size)
    // channel There will be three senders , So we also need to do three receiving 
    fmt.Println(<-size)
    fmt.Println(<-size)
    fmt.Println(<-size)
}

It can be done to main Further optimization

func main() {
    
    size:=make(chan int)
    urls := []string{
    "https://example.com/","http://golang.org","https://golang.org/doc"}
    for _,url := range urls {
    
        go reponseSize(url,size)
    }
    for i:=0;i<len(urls);i++ {
    
        fmt.Println(<-size)
    }
}

Update our channel To carry one struct

In order to send channel Contains more information . We can create one struct type , It can go through channel Send both together .

type Page struct {
    
    URL string
    Size int
}
func responseSize(url string, channel chan Page) {
    
    channel<-Page{
    URL: url,Size: len(body)}
}
func main() {
    
    pages:=make(chan Page)
    urls := []string{
    "https://example.com/","http://golang.org","https://golang.org/doc"}
    for _,url := range urls {
    
        go reponseSize(url,pages)
    }
    for i:=0;i<len(urls);i++ {
    
        page:=<-pages //  receive Page
        fmt.Printf("%s: %d\n",page.URL,page.Size)
    }
}

fourteen 、 Response request :Web Applications

Here is a use net/http A program that provides a simple response to a browser .

package main

import (
    "log"
    "net/http"
)
func viewHandler(writer http.ResponseWritter,request *http.Request) {
    
    message:=[]byte("Hello,Web!")
    _,err:=writter.Write(message) //  Add... To the response “Hello,web!”
    if err!=nil {
    
        log.Fatal(err)
    }
}
func main() {
    
    //  If you receive one with “/hello” At the end of the URL request , So called viewHandler Function to generate a response 
    http.HandleFunc("/hello",viewHandler)
    //  call http.ListenAndServe, It starts Web The server , Listen for browser requests , And respond to it 
    err:=http.ListenAndServer("localhost:8080",nil) //  In the second parameter nil Value only indicates that the pass through will be used HandleFunc Set the function to process the request .
    log.Fatal(err)
}

go run hello.go Run the server
We are running our own Web Applications ! Now we only need to connect one Web Browser and test . Open the browser , Enter this... In the address field URL.

http://localhost:8080/hello

Your computer is talking to itself

When starting our little Web Application time , It started its own Web The server , On your computer . Because the application is running on your computer ( Not somewhere on the Internet ), So we are URL Using a special host name in localhost. This tells the browser , It needs to establish a connection from your computer to the same computer .
In our code , We specify that the server should listen on the port 8080, So we include it in URL in , Just after the hostname .

Explain simple web Applications

because ListenAndServe Will run forever , Unless you encounter an error . If so , It will return the error , We will log this error before the program exits . however , If there are no mistakes , The program will continue to run , Until we press... At the terminal Ctrl-C To interrupt it .
And main comparison ,viewHandler Functions are nothing special . Server to the viewHandler Pass a http.ResponseWriter, Used to write data to the browser response , And a point http.Request Pointer to value , This value represents the browser's request .
stay viewHandler in , We call ResponseWriter Upper Write Method to add data to the response .Write String not accepted , But it accepts byte Value slice , So we will "Hello,web!" String conversion to []byte, Then pass it to Write.
ResponseWriter Of Write Method returns the number of bytes successfully written , And any errors encountered . We can't do anything useful with the number of bytes written , So we ignore it . But if something goes wrong , We will record it and exit the program .

The pattern to follow :HTML Templates

We will build a simple guestbook application for a website . Visitors will be able to enter information in the form of a form , The form will be saved to a file . They can also view a list of all previous signatures .

package main

import (
    "log"
    "net/http"
)
//  Transfer the code that reports the error to this function 
func check(err error) {
    
    if err!=nil {
    
        log.Fatal(err)
    }
}
func viewHandler(writer http.ResponseWriter, request *http.Request) {
    
    placeholder:=[]byte("signature list goes here")
    _,err:=write.Write(placeholder) //  Respond to 
    check(err)
}
func main() {
    
    http.HandleFunc("/guestbook",viewHandler)
    //  Set the server address and listening port 
    err:=http.ListenAndServer("localhost:8080",nil)
    log.Fatal(err)
}

Go Provides a html/template package , It will load... From the file HTML, And insert our signatures .

import (
    "html/template"
    "log"
    "net/http"
)
func viewHandler(writer http.ResponseWriter, request *http.Request) {
    
    //  Use view.html To create a new template 
    html,err:=template.ParseFiles("view.html") 
    check(err)
    //  Write the contents of the template to ResponseWriter
    err=html.Execute(writer,nil)
    check(err)
}

html/template Package based on text/template package . The method of using these two packages is almost identical , however html/template There are some additional security features , This is the use of HTML The required .

package main

import(
    "log"
    "os"
    "text/template"
)
func check(err error) {
    
    if err!=nil {
    
        log.Fatal(err)
    }
}
func main() {
    
    text:="Here's my template!\n"
    tmpl,err:=template.New("test").Parse(text) //  Create a new... Based on text Template value 
    check(err)
    //  Write the template to the terminal , instead of HTTP Respond to 
    err=tmpl.Execute(os.Stdout,nil) // Here's my template
    check(err)
}

http.ResponseWriter Values and os.Stdout All satisfied with io.Writer Interface , And can be passed to Template It's worth it Execute Method .Execute Will be called by... On any value passed to it Write Method to write the template .

Use action Insert data into the template

Template It's worth it Execute The second parameter of the method allows you to pass in the data to be inserted into the template . Its type is empty interface , This means that you can pass in any type of value .
To insert data into a template , You can add... To the template text action( operation ).action Use double curly brackets { {}} Express . In double curly braces , Specify the data to insert or the action to be performed by the template . Whenever the template encounters action when , It will calculate its contents , And in action Insert the result into the template text at the position of . In one operation , You can use a with “dot”( spot ) Of Execute Method to reference the data value passed to it .

func main() {
    
    templateText:="Template start\nAction:{
    {.}}\nTemplate end\n"
    teml,err:=template.New("test").Parse(templateText)
    check(err)
    err=tml.Execute(os.Stdout,"ABC")
}
// Template start
// Action: ABC

Use “range”action To repeat a part of the template

stay { {range}}action It corresponds to { {end}} Template section between tags , The array 、 section 、 Mapping or channel Repeat for each value collected in . Any operations in this section will also be repeated .
This template contains a { {range}}action, It will output each element in the slice . Before and after the loop , The value of the point will be the slice itself . But in the loop , The point refers to the current element of the slice . You will see this in the output .

func executeTemplate(text string, data interface{
    }) {
    
    tmpl,err:=template.New("test").Parse(text) //  Analyze the given text to create a template 
    check(err)
    err=tmpl.Execute(os.Stdout,data) //  stay action Use the given data value in 
    check(err) 
}
templateText:="Before loop: {
    {.}}\n{
    {range .}}In loop: {
    {.}}\n{
    {end}}After loop: {
    {.}}\n"
executeTemplate(templateText,[]string{
    "do","re","mi"})
// Before loop: [do re mi]
// In loop: do
// In loop: re
// In loop: mi
// After loop: [do re mi]

If provided to { {range}}action The value of is null or nil, Then the loop will not run at all .

Use action take struct Field insert template

type Part struct {
    
    Name string
    Count int
}
templateText:="Name:{
    {.Name}}\nCount:{
    {.Count}}/n"
executeTemplate(templateText,Part{
    Name:"Fuses,Count: 5"})

Save the signature and the number of signatures struct

type Guestbook struct {
    
    SignatureCount int
    Signature []string
}
func viewHandler(writer http.ResponseWriter,request *http.Request) {
    
    signature:=getStrings("signature.txt")
    html,err:=template.ParseFiles("view.html")
    check(err)
    guestbook:=Guestbook{
    
        SignatureCount:len(signature),
        Signature:signatures,
    }
    err=html.Execute(writer,guest(book))
    check(err)
}

Update the template to include signatures

Now let's update view.html Template text in to display a list of signatures .

<h1>Guestbook</h1>
<div>
    {
   {,SignatureCount}} total signature - 
    <a href="/guestbook/new">Add Your Signature</a>
</div>
<div>
    {
   {range .Signature}}
        <p>{
   {.}}</p>
    {
   {end}}
</div>

Allow users to use HTML Form add data

Next , We need to allow visitors to add their own signatures . We need to create one HTML Forms , So they can enter a signature in it . Forms usually provide one or more fields where users can enter data , And a submit button that allows users to send data to the server .
In the project directory , Use the following HTML The code creates a named new.html The file of . Here are some marks we haven't seen before .

<h1>Add a Signature</h1>

<from>
    <div><input type="text" name="signature"></div>
    <div><input type="submit"></div>
</from>

We've been view.html There is one of them. “Add Your Signature” link , It points to /guestbook/new route . Clicking this link will take you to a new path on the same server , Just like entering this URL:http://localhost:8080/guestbook/new.
In order for the server to provide newly accessed content , Add the following .

func newHandler(writer http.ResponseWriter, request *http.Request) {
    
    //  take new.html The content of the template is loaded as the text of the template 
    html,err:=template.ParseFiles("new.html")
    check(err)
    //  Write the template to the response ( There is no need to insert any data into it )
    err=html.Execute(writer,nil)
    check(err)
}
func main() {
    
    http.HandlFun("/guestbook",viewHandler)
    //  take newHandler Function to set the processing path to /guestbook/new Request 
    http.HandlerFunc("/guest/new",newHandler)
    err:=http.ListenAndServe("localhost:8080",nil)
    log.Fatal(err)
}

When someone visits /guestbook/new When the path , Whether you are typing directly or clicking on a link , A form for entering a signature is displayed . however , If you fill out the form and click Submit, Then nothing will happen .

For form submission Path and HTTP Method

Submitting the form actually requires two requests to the server : A request to get the form , Another request sends the user's data back to the server . Let's update the form HTML, To specify where and how the second request should be sent .
edit new.html And to from Element adds two new HTML attribute . The first property action Will specify the path to submit the request . To prevent the path from returning by default /guestbook/new, We will specify a new path :/guestbook/create.

<h1>Add a Signature</h1>
//  Submit form data to "/guestbook/create", As POST Submit instead of GET
<from action="/guestbook/create" method="POST">
    <div><input type="text" name="signature"></div>
    <div><input type="submit"></div>
</from>

by /guestbook/create Path setting a handler :createHandler(writer http.ResponseWriter, request *http.Request) .

func createHandler(writer http.ResponseWriter, request *http.Request) {
    
    signature:=request.FormValue("signature")
    options:=os.O_WRONLY|os.O_APPEND|os.O_CREAT
    //  Open file 
    file,err:=os.OpenFile("signature.txt",options,os.FileMode(0600))
    check(err)
    //  Write a signature on the new line of the file 
    _,err=fmt.Fprintln(file,signature)
    check(err)
    err=file.Close()
    check(err)
    //  Redirect to path 
    http.Redirect(writer,request,"/guestbook",http.StatusFound) //  Indicates the response code of the request 
}
原网站

版权声明
本文为[It's haohaozi]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121112269345.html