parent
865b824c09
commit
9b751c066c
@ -1,136 +0,0 @@ |
||||
package middleware |
||||
|
||||
import ( |
||||
"github.com/labstack/echo/v4" |
||||
"github.com/labstack/echo/v4/middleware" |
||||
"net/http" |
||||
) |
||||
|
||||
type CORSConfig struct { |
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper func(c echo.Context) bool |
||||
|
||||
// AllowOrigins determines the value of the Access-Control-Allow-Origin
|
||||
// response header. This header defines a list of origins that may access the
|
||||
// resource. The wildcard characters '*' and '?' are supported and are
|
||||
// converted to regex fragments '.*' and '.' accordingly.
|
||||
//
|
||||
// Security: use extreme caution when handling the origin, and carefully
|
||||
// validate any logic. Remember that attackers may register hostile domain names.
|
||||
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||
//
|
||||
// Optional. Default value []string{"*"}.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||
AllowOrigins []string |
||||
|
||||
// AllowOriginFunc is a custom function to validate the origin. It takes the
|
||||
// origin as an argument and returns true if allowed or false otherwise. If
|
||||
// an error is returned, it is returned by the handler. If this option is
|
||||
// set, AllowOrigins is ignored.
|
||||
//
|
||||
// Security: use extreme caution when handling the origin, and carefully
|
||||
// validate any logic. Remember that attackers may register hostile domain names.
|
||||
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||
//
|
||||
// Optional.
|
||||
AllowOriginFunc func(origin string) (bool, error) |
||||
|
||||
// AllowMethods determines the value of the Access-Control-Allow-Methods
|
||||
// response header. This header specified the list of methods allowed when
|
||||
// accessing the resource. This is used in response to a preflight request.
|
||||
//
|
||||
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
||||
// If `allowMethods` is left empty, this middleware will fill for preflight
|
||||
// request `Access-Control-Allow-Methods` header value
|
||||
// from `Allow` header that echo.Router set into context.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
||||
AllowMethods []string |
||||
|
||||
// AllowHeaders determines the value of the Access-Control-Allow-Headers
|
||||
// response header. This header is used in response to a preflight request to
|
||||
// indicate which HTTP headers can be used when making the actual request.
|
||||
//
|
||||
// Optional. Default value []string{}.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
|
||||
AllowHeaders []string |
||||
|
||||
// AllowCredentials determines the value of the
|
||||
// Access-Control-Allow-Credentials response header. This header indicates
|
||||
// whether the response to the request can be exposed when the
|
||||
// credentials mode (Request.credentials) is true. When used as part of a
|
||||
// response to a preflight request, this indicates whether or not the actual
|
||||
// request can be made using credentials. See also
|
||||
// [MDN: Access-Control-Allow-Credentials].
|
||||
//
|
||||
// Optional. Default value false, in which case the header is not set.
|
||||
//
|
||||
// Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
|
||||
// See "Exploiting CORS misconfigurations for Bitcoins and bounties",
|
||||
// https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
|
||||
AllowCredentials bool |
||||
|
||||
// UnsafeWildcardOriginWithAllowCredentials UNSAFE/INSECURE: allows wildcard '*' origin to be used with AllowCredentials
|
||||
// flag. In that case we consider any origin allowed and send it back to the client with `Access-Control-Allow-Origin` header.
|
||||
//
|
||||
// This is INSECURE and potentially leads to [cross-origin](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)
|
||||
// attacks. See: https://github.com/labstack/echo/issues/2400 for discussion on the subject.
|
||||
//
|
||||
// Optional. Default value is false.
|
||||
UnsafeWildcardOriginWithAllowCredentials bool |
||||
|
||||
// ExposeHeaders determines the value of Access-Control-Expose-Headers, which
|
||||
// defines a list of headers that clients are allowed to access.
|
||||
//
|
||||
// Optional. Default value []string{}, in which case the header is not set.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Header
|
||||
ExposeHeaders []string |
||||
|
||||
// MaxAge determines the value of the Access-Control-Max-Age response header.
|
||||
// This header indicates how long (in seconds) the results of a preflight
|
||||
// request can be cached.
|
||||
//
|
||||
// Optional. Default value 0. The header is set only if MaxAge > 0.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
||||
MaxAge int |
||||
} |
||||
|
||||
// DefaultCORSConfig is the default CORS middleware config.
|
||||
var DefaultCORSConfig = CORSConfig{ |
||||
Skipper: func(c echo.Context) bool { |
||||
return false |
||||
}, |
||||
AllowOrigins: []string{"*"}, |
||||
AllowMethods: []string{ |
||||
http.MethodGet, |
||||
http.MethodHead, |
||||
http.MethodPut, |
||||
http.MethodPatch, |
||||
http.MethodPost, |
||||
http.MethodDelete, |
||||
}, |
||||
} |
||||
|
||||
func (c *CORSConfig) ToMiddleware() echo.MiddlewareFunc { |
||||
return middleware.CORSWithConfig(middleware.CORSConfig{ |
||||
Skipper: c.Skipper, |
||||
AllowOrigins: c.AllowOrigins, |
||||
AllowOriginFunc: c.AllowOriginFunc, |
||||
AllowMethods: c.AllowMethods, |
||||
AllowHeaders: c.AllowHeaders, |
||||
AllowCredentials: c.AllowCredentials, |
||||
UnsafeWildcardOriginWithAllowCredentials: c.UnsafeWildcardOriginWithAllowCredentials, |
||||
ExposeHeaders: c.ExposeHeaders, |
||||
MaxAge: c.MaxAge, |
||||
}) |
||||
} |
||||
|
||||
func CORS() echo.MiddlewareFunc { |
||||
return DefaultCORSConfig.ToMiddleware() |
||||
} |
@ -1,4 +1,4 @@ |
||||
package middleware |
||||
package runtime |
||||
|
||||
import ( |
||||
"context" |
@ -0,0 +1,154 @@ |
||||
package rs |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"strings" |
||||
) |
||||
|
||||
var ( |
||||
// ErrOK 表示没有任何错误。
|
||||
// 对应 HTTP 响应状态码为 500。
|
||||
ErrOK = NewError(http.StatusOK, 0, "OK") |
||||
|
||||
// ErrInternal 客户端请求有效,但服务器处理时发生了意外。
|
||||
// 对应 HTTP 响应状态码为 500。
|
||||
ErrInternal = NewError(http.StatusInternalServerError, -100, "系统内部错误") |
||||
|
||||
// ErrServiceUnavailable 服务器无法处理请求,一般用于网站维护状态。
|
||||
// 对应 HTTP 响应状态码为 503。
|
||||
ErrServiceUnavailable = NewError(http.StatusServiceUnavailable, -101, "服务不可用") |
||||
|
||||
// ErrUnauthorized 用户未提供身份验证凭据,或者没有通过身份验证。
|
||||
// 响应的 HTTP 状态码为 401。
|
||||
ErrUnauthorized = NewError(http.StatusUnauthorized, -102, "身份验证失败") |
||||
|
||||
// ErrForbidden 用户通过了身份验证,但是不具有访问资源所需的权限。
|
||||
// 响应的 HTTP 状态码为 403。
|
||||
ErrForbidden = NewError(http.StatusForbidden, -103, "不具有访问资源所需的权限") |
||||
|
||||
// ErrGone 所请求的资源已从这个地址转移,不再可用。
|
||||
// 响应的 HTTP 状态码为 410。
|
||||
ErrGone = NewError(http.StatusGone, -104, "所请求的资源不存在") |
||||
|
||||
// ErrUnsupportedMediaType 客户端要求的返回格式不支持。
|
||||
// 比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式。
|
||||
// 响应的 HTTP 状态码为 415。
|
||||
ErrUnsupportedMediaType = NewError(http.StatusUnsupportedMediaType, -105, "请求的数据格式错误") |
||||
|
||||
// ErrUnprocessableEntity 无法处理客户端上传的附件,导致请求失败。
|
||||
// 响应的 HTTP 状态码为 422。
|
||||
ErrUnprocessableEntity = NewError(http.StatusUnprocessableEntity, -106, "上传了不被支持的附件") |
||||
|
||||
// ErrTooManyRequests 客户端的请求次数超过限额。
|
||||
// 响应的 HTTP 状态码为 429。
|
||||
ErrTooManyRequests = NewError(http.StatusTooManyRequests, -107, "请求次数超过限额") |
||||
|
||||
// ErrSeeOther 表示需要参考另一个 URL 才能完成接收的请求操作,
|
||||
// 当请求方式使用 POST、PUT 和 DELETE 时,对应的 HTTP 状态码为 303,
|
||||
// 其它的请求方式在大多数情况下应该使用 400 状态码。
|
||||
ErrSeeOther = NewError(http.StatusSeeOther, -108, "需要更进一步才能完成操作") |
||||
|
||||
// ErrBadRequest 服务器不理解客户端的请求。
|
||||
// 对应 HTTP 状态码为 400。
|
||||
ErrBadRequest = NewError(http.StatusBadRequest, -109, "请求错误") |
||||
|
||||
// ErrBadParams 客户端提交的参数不符合要求
|
||||
// 对应 HTTP 状态码为 400。
|
||||
ErrBadParams = NewError(http.StatusBadRequest, -110, "参数错误") |
||||
|
||||
// ErrRecordNotFound 访问的数据不存在
|
||||
// 对应 HTTP 状态码为 404。
|
||||
ErrRecordNotFound = NewError(http.StatusNotFound, -111, "访问的数据不存在") |
||||
) |
||||
|
||||
type Error struct { |
||||
// 被包装的错误对象
|
||||
internal error |
||||
// 响应的 HTTP 状态码
|
||||
status int |
||||
// 错误码
|
||||
code int |
||||
// 错误提示消息
|
||||
text string |
||||
// 错误携带的响应数据
|
||||
data any |
||||
} |
||||
|
||||
func NewError(status, code int, text string) *Error { |
||||
return &Error{nil, status, code, text, nil} |
||||
} |
||||
|
||||
// Code 返回错误码
|
||||
func (e *Error) Code() int { |
||||
return e.code |
||||
} |
||||
|
||||
// Text 返回错误提示
|
||||
func (e *Error) Text() string { |
||||
return e.text |
||||
} |
||||
|
||||
// Data 返回携带的数据
|
||||
func (e *Error) Data() any { |
||||
return e.data |
||||
} |
||||
|
||||
// Internal 返回原始错误
|
||||
func (e *Error) Internal() error { |
||||
return e.internal |
||||
} |
||||
|
||||
// Unwrap 支持 errors.Unwrap() 方法
|
||||
func (e *Error) Unwrap() error { |
||||
return e.Internal() |
||||
} |
||||
|
||||
// WithInternal 通过实际错误对象派生新的实例
|
||||
func (e *Error) WithInternal(err error) *Error { |
||||
// 由于错误比较复杂,不好做完全等于,
|
||||
// 在这里就直接复制当前对象
|
||||
c := *e |
||||
c.internal = err |
||||
return &c |
||||
} |
||||
|
||||
// WithStatus 通过 HTTP 状态码派生新的实例
|
||||
func (e *Error) WithStatus(status int) *Error { |
||||
if e.status != status { |
||||
c := *e |
||||
c.status = status |
||||
return &c |
||||
} |
||||
return e |
||||
} |
||||
|
||||
// WithText 通过新的错误提示派生新的实例
|
||||
func (e *Error) WithText(text string) *Error { |
||||
if text != e.text { |
||||
c := *e |
||||
c.text = text |
||||
return &c |
||||
} |
||||
return e |
||||
} |
||||
|
||||
// WithData 通过携带数据派生新的实例
|
||||
func (e *Error) WithData(data any) *Error { |
||||
if e.data != data { |
||||
c := *e |
||||
c.data = data |
||||
return &c |
||||
} |
||||
return e |
||||
} |
||||
|
||||
// String 实现 fmt.Stringer 接口
|
||||
func (e *Error) String() string { |
||||
return strings.TrimSpace(fmt.Sprintf("%d %s", e.code, e.text)) |
||||
} |
||||
|
||||
// Error 实现错误接口
|
||||
func (e *Error) Error() string { |
||||
return e.String() |
||||
} |
Loading…
Reference in new issue