В чем разница между ResponseWriter.Писать и Ио.WriteString?
Я видел три способа написания контента для ответа HTTP:
func Handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "blabla.n")
}
и:
func Handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("blablan"))
}
и здесь:
fmt.Fprintf(w, "blabla")
в чем разница между ними? Какой из них предпочтительнее использовать?
2 ответов
io.Writer
выходной поток представляет цель, в которую можно записать последовательность байтов. В Go это захвачено генералом io.Writer
интерфейс:
type Writer interface {
Write(p []byte) (n int, err error)
}
все, что имеет этот один Write()
метод может использоваться в качестве вывода, например файл на диске (os.File
), сетевое подключение (net.Conn
) или буфер в памяти (bytes.Buffer
).
в http.ResponseWriter
, который используется для настройки HTTP-ответа и отправки данных клиенту, также является таким io.Writer
, данные, которые вы хотите отправить (тело ответа) собирается путем вызова (не обязательно только один раз) ResponseWriter.Write()
(который должен реализовать общее io.Writer
). Это единственная гарантия, которую вы имеете о реализации http.ResponseWriter
интерфейс.
WriteString()
теперь WriteString()
. Часто мы хотим написать текстовые данные в io.Writer
. Да, мы можем сделать это, просто преобразовав string
до []byte
, например,
w.Write([]byte("Hello"))
, который работает, как ожидалось. Однако это очень частая операция, и поэтому для этого есть" общепринятый " метод, захваченный io.stringWriter
интерфейс неэкспортируется:
type stringWriter interface {
WriteString(s string) (n int, err error)
}
этот метод дает возможность написать string
значение вместо []byte
. Так что если что-то (что реализует io.Writer
) реализует этот метод, вы можете просто пройти string
значения без []byte
преобразования. это кажется незначительным упрощением кода, но это больше, чем это. преобразование string
to []byte
должен сделать копию string
содержание (потому что string
значения неизменяемы в Go), поэтому есть некоторые накладные расходы, которые становятся заметными, если string
"больше" и/или вы должны сделать это много раз.
в зависимости от характера и детали реализации io.Writer
, возможно, что можно написать содержание string
без преобразования его в []byte
и таким образом избежать указанных накладных.
в качестве примера, если io.Writer
- это то, что записывается в буфер памяти (bytes.Buffer
является таким примером), он может использовать встроенный copy()
функция:
встроенная функция копирования копирует элементы из исходного среза в целевой срез. (в частном случае он также будет копировать байты из строки в кусочек байта.)
на copy()
может использоваться для копирования содержимого (байтов)string
на []byte
без преобразования string
to []byte
, например:
buf := make([]byte, 100)
copy(buf, "Hello")
теперь есть функция "утилита"io.WriteString()
что пишет string
на io.Writer
. Но он делает это, сначала проверяя, прошел ли (динамический тип)io.Writer
есть WriteString()
метод, и если да, то это будет использоваться (чья реализация, вероятно более эффективный.) Если прошло io.Writer
не имеет такого метода, тогда генерал преобразовать-в-байт-ломтик-и-писать-это-то сторону метод будет использоваться как"запасной вариант".
вы можете подумать, что это WriteString()
будет преобладать только в случае буферов в памяти, но это не так. Ответы веб-запросов также часто буферизуются (с использованием буфера в памяти), поэтому он может повысить производительность в случае http.ResponseWriter
тоже. И если вы посмотрите на реализацию http.ResponseWriter
: это несообщаемый тип http.response
(server.go
в настоящее время строка #308), которая реализует WriteString()
(в настоящее время строка #1212), поэтому это означает улучшение.
в целом, когда вы пишите string
значения, рекомендуется использовать io.WriteString()
как это может быть более эффективным (быстрее).
fmt.Fprintf()
вы должны смотреть на это как на удобный и простой способ добавить больше форматирования к данным, которые вы хотите написать, в обмен на несколько меньше производительным.
так что используйте fmt.Fprintf()
если вы хотите отформатировать string
создано простым способом, например:
name := "Bob"
age := 23
fmt.Fprintf(w, "Hi, my name is %s and I'm %d years old.", name, age)
что приведет к следующему string
будет написано:
Hi, my name is Bob and I'm 23 years old.
одна вещь, вы не должны забывать: fmt.Fprintf()
ждет формат строки, поэтому он будет предварительно обработан и не будет записан как есть на выходе. В качестве быстрого примера:
fmt.Fprintf(w, "100 %%")
этого следовало ожидать "100 %%"
было бы написано в выход (с 2 %
символов), но только один будет отправлен как в строке формата %
является специальным символом и %%
только в выходной.
если вы просто хотите написать string
С помощью fmt
пакета, используйте fmt.Fprint()
который не требует формат string
:
fmt.Fprint(w, "Hello")
еще одно преимущество использования fmt
пакет заключается в том, что вы можете писать значения других типов, а не только string
s, например,
fmt.Fprint(w, 23, time.Now())
(конечно, правила преобразования любого значения в string
- и серии байтов в конечном итоге-хорошо определен, в doc fmt
пакета.)
для "простых" форматированный вывод fmt
пакет может быть в порядке. Для сложных выходных документов рассмотрите возможность использования text/template
(для общего текста) и html/template
(всякий раз, когда вывод HTML).
передача / передача http.ResponseWriter
для полноты следует отметить, что часто контент, который вы хотите отправить в качестве веб-ответа, генерируется "чем-то", поддерживающим" потоковое " результат. Примером может быть ответ JSON, который генерируется из структуры или карты.
в таких случаях часто более эффективно передать / передать ваш http.ResponseWriter
что это io.Writer
этой что-то если он поддерживает запись результата io.Writer
на ходу.
хорошим примером этого является генерация ответов JSON. Конечно, вы можете маршалировать объект в JSON с помощью json.Marshal()
, который возвращает вам байтовый срез, который вы можете просто отправить, вызвав ResponseWriter.Write()
.
тем не менее, это более эффективно, чтобы json
пакет знайте, что у вас есть io.Writer
, и в конечном итоге вы хотите отправить результат на это. Таким образом, нет необходимости сначала генерировать текст JSON в буфере, который вы просто пишете в ваш ответ, а затем отбросить. Вы можете создать новый json.Encoder
по телефону json.NewEncoder()
к которому вы можете пройти ваш http.ResponseWriter
как io.Writer
, а вызов Encoder.Encode()
после этого будет непосредственно записывать результат JSON в ваш писатель ответов.
одним из недостатков здесь является то, что при сбое генерации ответа JSON у вас может быть частично отправленный / зафиксированный ответ, который вы не можете вернуть. Если это проблема для вас, вы на самом деле у вас нет выбора, кроме как генерировать ответ в буфере, и если маршалинг удастся, вы можете написать полный ответ сразу.
как вы можете видеть из здесь(ResponseWriter), что это интерфейс с Write([]byte) (int, error)
метод.
так io.WriteString
и fmt.Fprintf
как принимают писатель как 1-й аргумент, который также является оберткой интерфейса Write(p []byte) (n int, err error)
метод
type Writer interface {
Write(p []byte) (n int, err error)
}
поэтому, когда вы вызываете io.WriteString (w,"blah") регистрация здесь
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}
или fmt.Fprintf (w, "blabla") регистрация здесь
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
}
вы просто вызов метода Write косвенно при передаче ResponseWriter
переменной в обоих методах.
так почему бы не вызвать его напрямую с помощью w.Write([]byte("blabla\n"))
. Надеюсь, ты получил ответ.
PS: есть также другой способ использовать это, если вы хотите отправить его как ответ JSON.
json.NewEncoder(w).Encode(wrapper) //Encode take interface as arg. wrapper can be wrapper := SuccessResponseWrapper{Success:true, Data:data}