github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2015/go-for-java-programmers/frontend.go (about)

     1  // +build OMIT
     2  
     3  // The server program issues Google search requests. It serves on port 8080.
     4  //
     5  // The /search endpoint accepts these query params:
     6  //   q=the Google search query
     7  //
     8  // For example, http://localhost:8080/search?q=golang serves the first
     9  // few Google search results for "golang".
    10  package main
    11  
    12  import (
    13  	"encoding/json"
    14  	"fmt"
    15  	"html/template"
    16  	"log"
    17  	"net/http"
    18  	"net/url"
    19  	"time"
    20  )
    21  
    22  func main() {
    23  	http.HandleFunc("/search", handleSearch) // HL
    24  	fmt.Println("serving on http://localhost:8080/search")
    25  	log.Fatal(http.ListenAndServe("localhost:8080", nil))
    26  }
    27  
    28  // handleSearch handles URLs like "/search?q=golang" by running a
    29  // Google search for "golang" and writing the results as HTML to w.
    30  func handleSearch(w http.ResponseWriter, req *http.Request) {
    31  	log.Println("serving", req.URL)
    32  
    33  	// Check the search query.
    34  	query := req.FormValue("q") // HL
    35  	if query == "" {
    36  		http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest)
    37  		return
    38  	}
    39  	// ENDQUERY OMIT
    40  
    41  	// Run the Google search.
    42  	start := time.Now()
    43  	results, err := Search(query) // HL
    44  	elapsed := time.Since(start)
    45  	if err != nil {
    46  		http.Error(w, err.Error(), http.StatusInternalServerError)
    47  		return
    48  	}
    49  	// ENDSEARCH OMIT
    50  
    51  	// Render the results.
    52  	type templateData struct {
    53  		Results []Result
    54  		Elapsed time.Duration
    55  	}
    56  	if err := resultsTemplate.Execute(w, templateData{ // HL
    57  		Results: results,
    58  		Elapsed: elapsed,
    59  	}); err != nil {
    60  		log.Print(err)
    61  		return
    62  	}
    63  	// ENDRENDER OMIT
    64  }
    65  
    66  // A Result contains the title and URL of a search result.
    67  type Result struct { // HL
    68  	Title, URL string // HL
    69  } // HL
    70  
    71  var resultsTemplate = template.Must(template.New("results").Parse(`
    72  <html>
    73  <head/>
    74  <body>
    75    <ol>
    76    {{range .Results}}
    77      <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li>
    78    {{end}}
    79    </ol>
    80    <p>{{len .Results}} results in {{.Elapsed}}</p>
    81  </body>
    82  </html>
    83  `))
    84  
    85  // Search sends query to Google search and returns the results.
    86  func Search(query string) ([]Result, error) {
    87  	// Prepare the Google Search API request.
    88  	u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0")
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	q := u.Query()
    93  	q.Set("q", query) // HL
    94  	u.RawQuery = q.Encode()
    95  
    96  	// Issue the HTTP request and handle the response.
    97  	resp, err := http.Get(u.String()) // HL
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	defer resp.Body.Close() // HL
   102  
   103  	// Parse the JSON search result.
   104  	// https://developers.google.com/web-search/docs/#fonje
   105  	var jsonResponse struct {
   106  		ResponseData struct {
   107  			Results []struct {
   108  				TitleNoFormatting, URL string
   109  			}
   110  		}
   111  	}
   112  	if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil { // HL
   113  		return nil, err
   114  	}
   115  
   116  	// Extract the Results from jsonResponse and return them.
   117  	var results []Result
   118  	for _, r := range jsonResponse.ResponseData.Results { // HL
   119  		results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL})
   120  	}
   121  	return results, nil
   122  }