rui

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2021 License: MIT Imports: 15 Imported by: 0

README

rui

介绍

很轻量级 web api 开发库,全部调用的官方 http 库。 支持 GET,POST,PUT,DELETE,OPTIONS,HEAD,PATCH 请求分发。 路由使用的 map 的方式,所以是不支持路径作为参数/user/*/info的方式的。

安装教程

go mod 的,可以忽略下面这个。

go get gitee.com/ruige_fun/rui
go get gitee.com/ruige_fun/rlog

gitee.com/ruige_fun/rlog 是日志打印相关的,这个你也可以使用自己定义的。

基本使用
package main

import (
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Get("/ping", func(ctx rui.Context) {
		ctx.RespJson(
			map[string]interface{}{
				"ip":ctx.GetIPv4(),
			})
	})
	_ = app.Run("", 80)
}

浏览器打开:http://127.0.0.1:80/ping

即可看到以下结果:

{"ip":"127.0.0.1"}
自定义日志
package main

import (
	"gitee.com/ruige_fun/rui"
	"log"
)

func main(){
	app := rui.New(Info, Warn, Error)
	app.Get("/ping", func(ctx rui.Context) {
		ctx.RespJson(
			map[string]interface{}{
				"ip":ctx.GetIPv4(),
			})
	})
	_ = app.Run("", 80)
}

func Info(v ...interface{}){
	log.Println("基本信息:",f,v)
}

func Warn(v ...interface{}){
	log.Println("警告信息:",f,v)
}

func Error(v ...interface{}){
	log.Println("错误信息:",f,v)
}
支持跨域
package main

import (
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Use(rui.UseCORS) //支持跨域
	app.Get("/ping", func(ctx rui.Context) {
		ctx.RespJson(
			map[string]interface{}{
				"ip":ctx.GetIPv4(),
			})
	})
	_ = app.Run("", 80)
}
路由注册

目前支持:GETPOSTPUTDELETEOPTIONSHEADPATCH 请求的注册。

package main

import (
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Use(rui.UseCORS) //支持跨域
	app.Get("/get", ping) //注册GET请求
	app.Post("/post", ping) //注册POST请求
	app.Delete("/delete", ping) //注册DELETE请求
	_ = app.Run("", 80)
}

func ping(ctx rui.Context) {
	ctx.RespJson(
		map[string]interface{}{
			"ip":ctx.GetIPv4(),
			"path":ctx.Request().URL.Path,
			"method":ctx.Request().Method,
		})
}
使用原生http的参数
package main

import (
	"fmt"
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Get("/ping", ping) //注册GET请求
	_ = app.Run("", 80)
}

func ping(ctx rui.Context) {
	writer := ctx.ResponseWriter() //原生ResponseWriter
	request := ctx.Request() //原生Request
	_, _ = writer.Write([]byte(fmt.Sprint(request.URL)))
}
静态文件服务
package main

import (
	"fmt"
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Get("/ping", ping) //注册GET请求
	app.Static("/static/", "./public") //注册静态文件服务,将所有 /static/ 开头的请求,使用静态文件服务。映射 ./public 文件夹
	_ = app.Run("", 80)
}

func ping(ctx rui.Context) {
	writer := ctx.ResponseWriter() //原生ResponseWriter
	request := ctx.Request() //原生Request
	_, _ = writer.Write([]byte(fmt.Sprint(request.URL)))
}
body请求体json绑定

读取请求的body,并且json解析到结构体。

请求
curl -X POST -i 'http://127.0.0.1:80/ping' --data '{"code":0,"msg":"消息","data":"数据"}'
代码
package main

import (
	"fmt"
	"gitee.com/ruige_fun/rui"
	"gitee.com/ruige_fun/rlog"
)

func main(){
	app := rui.New(rlog.Info, rlog.Warn, rlog.Error)
	app.Any("/ping", ping) //注册 所有 类型的请求。
	_ = app.Run("", 80)
}

type MSG struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

func ping(ctx rui.Context) {
	var msg MSG
	_ = ctx.GetBodyJsonBind(&msg)
	fmt.Println(msg)
	ctx.RespSTD(0, "", ctx.GetIPAndPort())
}
注册及中间件方法
//Serve 服务
type Serve interface {
	//Use 添加中间件函数,中间件函数返回false则不会继续执行,返回true则会继续往下执行。
	Use(fx func(ctx Context) bool)

	//Static 不过中间件的静态文件服务,调用的http.FileServer
	//route 请求路径前缀
	//dirPath 映射的文件夹
	Static(route string, dirPath string)

	//StaticUse 过中间件的静态文件服务,调用的http.ServeContent
	//route 请求路径前缀
	//dirPath 映射的文件夹
	//注意,该方式是以文件流的方式,返回数据。
	StaticUse(route string, dirPath string)

	//Get 注册 GET 请求方法的处理器
	Get(route string, fx func(Context))

	//Post 注册 POST 请求方法的处理器
	Post(route string, fx func(Context))

	//Put 注册 PUT 请求方法的处理器
	Put(route string, fx func(Context))

	//Delete 注册 DELETE 请求方法的处理器
	Delete(route string, fx func(Context))

	//Patch 注册 PATCH 请求方法的处理器
	Patch(route string, fx func(Context))

	//Head 注册 HEAD 请求方法的处理器
	Head(route string, fx func(Context))

	//Options 注册 OPTIONS 请求方法的处理器
	Options(route string, fx func(Context))

	//Any 注册一个支持所有请求方法的处理器
	Any(route string, fx func(Context))

	//Run 运行实例
	//ip 绑定的ip地址,如果为空,则绑定默认本地地址
	//port 监听的端口号
	Run(ip string, port uint) error
}
上下文处理方法
//Context 上下文处理
type Context interface {

	//ResponseWriter 返回原生的http.ResponseWriter
	ResponseWriter() http.ResponseWriter

	//Request 返回原生的*http.Request
	Request() *http.Request

	//GetHeaderRQ 返回原生的*http.Request.Header
	GetHeaderRQ() *http.Header

	//GetHeaderRW 返回原生的http.ResponseWriter.Header
	GetHeaderRW() http.Header

	//GetIPAndPort 获取IP地址和端口号,比如127.0.0.1:12345
	GetIPAndPort() string

	//GetIPv4 获取IP地址,比如127.0.0.1
	GetIPv4() string

	//GetIPv4XRealIP 获取首次的IP地址,需要反向代理服务器,添加请求头,否则获取结果是空的
	GetIPv4XRealIP() string

	//GetIPv4XForwardedFor 获取父级反向代理IP地址
	//获取来源地址,如果只有一个反向代理服务器,那就返回客户机IPv4地址。
	//多个反向代理,那返回的是反向代理服务器的地址。
	//比如:127.0.0.1
	GetIPv4XForwardedFor() string

	//GetXForwardedProto 获取转发协议,比如:https
	//实际就是获取请求头X-Forwarded-Proto字段
	GetXForwardedProto() string

	//GetBody 获取请求主体的bytes
	GetBody() []byte

	//GetBodyJsonBind 获取请求主体,并且将内容json解码到参数v里面。
	//v 必须是结构体指针,并且不能为nil
	GetBodyJsonBind(v interface{}) error

	//GetPath 获取请求path
	//比如url是 /hello?a=1&b=2&c=3
	//则获取的结果是:/hello
	GetPath() string

	//GetURL 获取请求URL
	//比如url是 /hello?a=1&b=2&c=3
	GetURL() string

	//HeaderSet 设置响应头,调用的Set
	//k 字段名
	//v 值
	HeaderSet(k string, v string)

	//HeaderGet 获取响应头,调用的Get
	//k 字段名
	HeaderGet(k string) string

	//HeaderAdd 添加响应头,调用的Add
	//k 字段名
	//v 值
	HeaderAdd(k string, v string)

	//HeaderDel 删除响应头,调用的Del
	//k 字段名
	HeaderDel(k string)

	//Write 响应body
	//body 响应主体的bytes
	Write(body []byte)

	//WriteAndContentType 响应body并且设置content-type,如果content-type为空,则不设置
	//body 响应主体的bytes
	//contentType 响应头Content-Type的值
	WriteAndContentType(body []byte, contentType string)

	//WriteHeaderCode 响应状态码
	//statusCode 状态码
	WriteHeaderCode(statusCode int)

	//RespStatusCode 响应状态码
	//statusCode 状态码
	RespStatusCode(statusCode int)

	//RespSTD 响应标准的json
	//code 消息状态码,注意,不是响应状态码
	//msg 消息提示内容
	//data 数据内容
	//比如:{"code":0,"msg":"消息内容","data":"数据内容"}
	RespSTD(code int, msg string, data interface{})

	//RespString 响应body
	//body 响应主体的string
	RespString(body string)

	//RespBytes 响应body
	//body 响应主体的bytes
	RespBytes(body []byte)

	//RespBytesAndContentType 响应body并且设置content-type,如果content-type为空,则不设置
	//body 响应主体的bytes
	//contentType 响应头Content-Type的值
	RespBytesAndContentType([]byte, string)

	//RespJsonBytes 响应body,会自动添加content-type
	//bodyJson 响应体的bytes,必须是json哟
	RespJsonBytes(bodyJson []byte)

	//RespJson 响应body,会自动添加content-type
	//mp 字段数据,会转成json,然后响应返回
	RespJson(map[string]interface{})

	//FormValue 获取表单字段,也就是url参数
	//key 表单字段名
	FormValue(key string) string

	//FormValueString 获取请求表单字段的值,并且转换成string类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueString(key string, def ...string) string

	//FormValueInt 获取请求表单字段的值,并且转换成int类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueInt(key string, def ...int) int

	//FormValueInt64 获取请求表单字段的值,并且转换成int64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueInt64(key string, def ...int64) int64

	//FormValueUint 获取请求表单字段的值,并且转换成uint类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueUint(key string, def ...uint) uint

	//FormValueUint64 获取请求表单字段的值,并且转换成uint64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueUint64(key string, def ...uint64) uint64

	//FormValueFloat64 获取请求表单字段的值,并且转换成float64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueFloat64(key string, def ...float64) float64

	//FormValueBool 获取请求表单字段的值,并且转换成bool类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueBool(key string, def ...bool) bool

	//PostFormValue 获取表单字段,POST请求表单
	//key 表单字段名
	PostFormValue(key string) string

	//PostFormValueString 获取请求表单字段的值,并且转换成string类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueString(key string, def ...string) string

	//PostFormValueInt 获取请求表单字段的值,并且转换成int类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueInt(key string, def ...int) int

	//PostFormValueInt64 获取请求表单字段的值,并且转换成int64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueInt64(key string, def ...int64) int64

	//PostFormValueUint 获取请求表单字段的值,并且转换成uint类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueUint(key string, def ...uint) uint

	//PostFormValueUint64 获取请求表单字段的值,并且转换成uint64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueUint64(key string, def ...uint64) uint64

	//PostFormValueFloat64 获取请求表单字段的值,并且转换成float64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueFloat64(key string, def ...float64) float64

	//PostFormValueBool 获取请求表单字段的值,并且转换成bool类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueBool(key string, def ...bool) bool

	//FormFile 获取表单上传的文件
	//key 表单文件的字段名
	FormFile(string) (multipart.File, *multipart.FileHeader, error)

	//FormFileSave 获取表单上传的文件并保存,返回文件名sha256值+扩展名
	//key 表单文件的字段名
	//savePath 存储文件的文件夹路径
	//返回保存的文件名,不含savePath,文件名由 sha256 + 文件扩展名 组成
	FormFileSave(string, string) (string, error)

	//FormFilesSave 接收多文件并且存储,返回文件名sha256值+扩展名
	//savePath 存储文件的文件夹路径
	//返回保存的文件名,不含savePath,文件名由 sha256 + 文件扩展名 组成
	FormFilesSave(string) ([]string, error) //获取并保存多个文件,适合多文件上传
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func UseCORS

func UseCORS(ctx Context) bool

UseCORS 支持跨域请求的中间件

Types

type Context

type Context interface {

	//ResponseWriter 返回原生的http.ResponseWriter
	ResponseWriter() http.ResponseWriter

	//Request 返回原生的*http.Request
	Request() *http.Request

	//GetHeaderRQ 返回原生的*http.Request.Header
	GetHeaderRQ() *http.Header

	//GetHeaderRW 返回原生的http.ResponseWriter.Header
	GetHeaderRW() http.Header

	//GetIPAndPort 获取IP地址和端口号,比如127.0.0.1:12345
	GetIPAndPort() string

	//GetIPv4 获取IP地址,比如127.0.0.1
	GetIPv4() string

	//GetIPv4XRealIP 获取首次的IP地址,需要反向代理服务器,添加请求头,否则获取结果是空的
	GetIPv4XRealIP() string

	//GetIPv4XForwardedFor 获取父级反向代理IP地址
	//获取来源地址,如果只有一个反向代理服务器,那就返回客户机IPv4地址。
	//多个反向代理,那返回的是反向代理服务器的地址。
	//比如:127.0.0.1
	GetIPv4XForwardedFor() string

	//GetXForwardedProto 获取转发协议,比如:https
	//实际就是获取请求头X-Forwarded-Proto字段
	GetXForwardedProto() string

	//GetBody 获取请求主体的bytes
	GetBody() []byte

	//GetBodyJsonBind 获取请求主体,并且将内容json解码到参数v里面。
	//v 必须是结构体指针,并且不能为nil
	GetBodyJsonBind(v interface{}) error

	//GetPath 获取请求path
	//比如url是 /hello?a=1&b=2&c=3
	//则获取的结果是:/hello
	GetPath() string

	//GetURL 获取请求URL
	//比如url是 /hello?a=1&b=2&c=3
	GetURL() string

	//HeaderSet 设置响应头,调用的Set
	//k 字段名
	//v 值
	HeaderSet(k string, v string)

	//HeaderGet 获取响应头,调用的Get
	//k 字段名
	HeaderGet(k string) string

	//HeaderAdd 添加响应头,调用的Add
	//k 字段名
	//v 值
	HeaderAdd(k string, v string)

	//HeaderDel 删除响应头,调用的Del
	//k 字段名
	HeaderDel(k string)

	//Write 响应body
	//body 响应主体的bytes
	Write(body []byte)

	//WriteAndContentType 响应body并且设置content-type,如果content-type为空,则不设置
	//body 响应主体的bytes
	//contentType 响应头Content-Type的值
	WriteAndContentType(body []byte, contentType string)

	//WriteHeaderCode 响应状态码
	//statusCode 状态码
	WriteHeaderCode(statusCode int)

	//RespStatusCode 响应状态码
	//statusCode 状态码
	RespStatusCode(statusCode int)

	//RespSTD 响应标准的json
	//code 消息状态码,注意,不是响应状态码
	//msg 消息提示内容
	//data 数据内容
	//比如:{"code":0,"msg":"消息内容","data":"数据内容"}
	RespSTD(code int, msg string, data interface{})

	//RespString 响应body
	//body 响应主体的string
	RespString(body string)

	//RespBytes 响应body
	//body 响应主体的bytes
	RespBytes(body []byte)

	//RespBytesAndContentType 响应body并且设置content-type,如果content-type为空,则不设置
	//body 响应主体的bytes
	//contentType 响应头Content-Type的值
	RespBytesAndContentType([]byte, string)

	//RespJsonBytes 响应body,会自动添加content-type
	//bodyJson 响应体的bytes,必须是json哟
	RespJsonBytes(bodyJson []byte)

	//RespJson 响应body,会自动添加content-type
	//mp 字段数据,会转成json,然后响应返回
	RespJson(map[string]interface{})

	//FormValue 获取表单字段,也就是url参数
	//key 表单字段名
	FormValue(key string) string

	//FormValueString 获取请求表单字段的值,并且转换成string类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueString(key string, def ...string) string

	//FormValueInt 获取请求表单字段的值,并且转换成int类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueInt(key string, def ...int) int

	//FormValueInt64 获取请求表单字段的值,并且转换成int64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueInt64(key string, def ...int64) int64

	//FormValueUint 获取请求表单字段的值,并且转换成uint类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueUint(key string, def ...uint) uint

	//FormValueUint64 获取请求表单字段的值,并且转换成uint64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueUint64(key string, def ...uint64) uint64

	//FormValueFloat64 获取请求表单字段的值,并且转换成float64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueFloat64(key string, def ...float64) float64

	//FormValueBool 获取请求表单字段的值,并且转换成bool类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	FormValueBool(key string, def ...bool) bool

	//PostFormValue 获取表单字段,POST请求表单
	//key 表单字段名
	PostFormValue(key string) string

	//PostFormValueString 获取请求表单字段的值,并且转换成string类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueString(key string, def ...string) string

	//PostFormValueInt 获取请求表单字段的值,并且转换成int类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueInt(key string, def ...int) int

	//PostFormValueInt64 获取请求表单字段的值,并且转换成int64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueInt64(key string, def ...int64) int64

	//PostFormValueUint 获取请求表单字段的值,并且转换成uint类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueUint(key string, def ...uint) uint

	//PostFormValueUint64 获取请求表单字段的值,并且转换成uint64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueUint64(key string, def ...uint64) uint64

	//PostFormValueFloat64 获取请求表单字段的值,并且转换成float64类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueFloat64(key string, def ...float64) float64

	//PostFormValueBool 获取请求表单字段的值,并且转换成bool类型
	//key 要获取的字段
	//def 默认值,如果获取获取失败,则返回默认值
	PostFormValueBool(key string, def ...bool) bool

	//FormFile 获取表单上传的文件
	//key 表单文件的字段名
	FormFile(string) (multipart.File, *multipart.FileHeader, error)

	//FormFileSave 获取表单上传的文件并保存,返回文件名sha256值+扩展名
	//key 表单文件的字段名
	//savePath 存储文件的文件夹路径
	//返回保存的文件名,不含savePath,文件名由 sha256 + 文件扩展名 组成
	FormFileSave(string, string) (string, error)

	//FormFilesSave 接收多文件并且存储,返回文件名sha256值+扩展名
	//savePath 存储文件的文件夹路径
	//返回保存的文件名,不含savePath,文件名由 sha256 + 文件扩展名 组成
	FormFilesSave(string) ([]string, error) //获取并保存多个文件,适合多文件上传
}

Context 上下文处理

type Serve added in v1.0.2

type Serve interface {
	//Use 添加中间件函数,中间件函数返回false则不会继续执行,返回true则会继续往下执行。
	Use(fx func(ctx Context) bool)

	//Static 不过中间件的静态文件服务,调用的http.FileServer
	//route 请求路径前缀
	//dirPath 映射的文件夹
	Static(route string, dirPath string)

	//StaticUse 过中间件的静态文件服务,调用的http.ServeContent
	//route 请求路径前缀
	//dirPath 映射的文件夹
	//注意,该方式是以文件流的方式,返回数据。
	StaticUse(route string, dirPath string)

	//Get 注册 GET 请求方法的处理器
	Get(route string, fx func(Context))

	//Post 注册 POST 请求方法的处理器
	Post(route string, fx func(Context))

	//Put 注册 PUT 请求方法的处理器
	Put(route string, fx func(Context))

	//Delete 注册 DELETE 请求方法的处理器
	Delete(route string, fx func(Context))

	//Patch 注册 PATCH 请求方法的处理器
	Patch(route string, fx func(Context))

	//Head 注册 HEAD 请求方法的处理器
	Head(route string, fx func(Context))

	//Options 注册 OPTIONS 请求方法的处理器
	Options(route string, fx func(Context))

	//Any 注册一个支持所有请求方法的处理器
	Any(route string, fx func(Context))

	//Run 运行实例
	//ip 绑定的ip地址,如果为空,则绑定默认本地地址
	//port 监听的端口号
	Run(ip string, port uint) error

	//RunTLS 运行实例
	//ip 绑定的ip地址,如果为空,则绑定默认本地地址
	//port 监听的端口号
	//certFile 证书文件地址
	//keyFile 密钥文件地址
	RunTLS(addr string, port uint, certFile string, keyFile string) error
}

Serve 服务

func New

func New(Info, Warn, Error func(v ...interface{})) Serve

New 创建一个服务实例,并且设置日志打印函数,如果日志参数为nil,则不打印该等级的日志

func NewDefault

func NewDefault() Serve

NewDefault 创建一个服务实例,日志打印默认使用log.Println

func NewNotPrintLog

func NewNotPrintLog() Serve

NewNotPrintLog 创建一个服务实例,不打印任何服务日志

type StdMsg added in v1.0.2

type StdMsg struct {
	Code int         `json:"code"` //消息状态码,注意,不是响应状态码
	Msg  string      `json:"msg"`  //消息提示内容
	Data interface{} `json:"data"` //数据内容,可以是普通变量,可以是结构体,可以是切片或map等等
}

func (StdMsg) EncodingJson added in v1.0.2

func (msg StdMsg) EncodingJson() []byte

EncodingJson 将结构体,json编码并且返回 如果编码失败,则返回 {"code":-1,"msg":"服务器json编码失败",data:""}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL