代码拉取完成,页面将自动刷新
package openapi
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"gitee.com/bjf-fhe/apinx/errors"
"github.com/sirupsen/logrus"
"github.com/getkin/kin-openapi/openapi3"
)
// type ContentTestFn func(def *openapi3.Schema, value interface{}) (bool, interface{}, error)
func GetBaseUrlFromServer(serv *openapi3.Server, args map[string]string) string {
baseURL := serv.URL
for k, v := range args {
matcher := regexp.MustCompile("{\\s*" + k + "\\s*}")
baseURL = matcher.ReplaceAllString(baseURL, v)
}
return baseURL
}
//CallByMethod 根据方法进行调用
func CallByMethodAndReturnBody(method *openapi3.Operation, nameOfPath, nameOfMethod string, server *openapi3.Server, args *CallConfig, fn DefHandler) (hasChange bool, body io.ReadCloser, extraInfos *TestResult, mSel *OperationSelection, statuscode int, err error) {
statuscode = 500
extraInfos = new(TestResult)
baseURL := GetBaseUrlFromServer(server, args.Selection.GetServerVariables())
// var mSel *calling.OperationSelection
if path, ok := args.Selection.GetPathArgs(nameOfPath); ok {
mSel = path.GetByMethod(nameOfMethod)
}
if mSel == nil {
mSel = new(OperationSelection)
}
baseURL += nameOfPath
var u *url.URL
u, err = url.Parse(baseURL)
if err != nil {
return
}
var q = u.Query()
// var reqMethod = args.Selection.
if args.Session != nil {
q.Set("bjf-source", args.Session.Source)
q.Set("bjf-source-sign", args.Session.SourceSign)
}
//TODO 根据请求类型处理头部信息
//处理parameters
for index, par := range method.Parameters {
var value interface{}
if len(mSel.Parameters) > index && mSel.Parameters[index].Fixed {
//设置为固定值时,获取固定值
value = mSel.Parameters[index].Value
} else if args.InputType == InputType_AsStandard {
if val, ok := args.Input[par.Value.Name]; ok {
//从input中获取
value = val
goto setValue
}
} else {
//无论是raw map还是纯raw,都是以这种模式获取参数
switch par.Value.In {
case "header":
if values, ok := args.Header[par.Value.Name]; ok {
value = values[0]
goto setValue
}
case "query":
if args.Query.Has(par.Value.Name) {
value = args.Query.Get(par.Value.Name)
goto setValue
}
case "path":
if args.PathVars.Has(par.Value.Name) {
value = args.PathVars.Get(par.Value.Name)
goto setValue
}
case "cookie":
//TODO
}
}
if args.InputType == InputType_AsWholeRaw {
if val, ok := args.Input[par.Value.Name]; ok {
//从input中获取
value = val
}
} else if par.Value.Schema != nil && par.Value.Schema.Value != nil {
//设置为default值
value = par.Value.Schema.Value.Default
}
setValue:
switch par.Value.In {
case "query":
q.Set(par.Value.Name, EncodingParToString(nil, par.Value.Style, value))
case "path":
//替换路径中的参数
matcher := regexp.MustCompile("{\\s*" + par.Value.Name + "\\s*}")
u.Path = matcher.ReplaceAllString(u.Path, EncodingParToString(nil, par.Value.Style, value))
default:
//TODO 补充其他位置的参数处理
}
}
//处理body
var requestBts []byte
ctType := args.RequestType
if nameOfMethod == "post" || nameOfMethod == "put" || nameOfMethod == "patch" {
//处理请求
var requestMap map[string]interface{}
var rawRequest interface{}
var reqDef *openapi3.MediaType
if mSel.RequestBody == nil {
mSel.RequestBody = &RequestBodySelection{
Default: args.RequestType,
}
}
if method.RequestBody != nil && method.RequestBody.Value != nil && method.RequestBody.Value.Content != nil {
reqDef = method.RequestBody.Value.Content.Get(mSel.RequestBody.Default)
}
if reqDef == nil {
if fn.CreateResponseIfNotDefined() {
mSel.RequestBody.Default = ctType
if mSel.RequestBody == nil {
mSel.RequestBody = new(RequestBodySelection)
}
var raw = args.RequestBody
var inputType string
var schema *openapi3.Schema
if ctType == "application/json" {
var bts = raw.([]byte)
if bts[0] == '{' {
inputType = "object"
} else if bts[0] == '[' {
inputType = "array"
} else if bts[0] == '"' {
inputType = "string"
} else if bts[0] == 't' || bts[0] == 'f' {
inputType = "boolean"
} else if bts[0] >= '0' && bts[0] <= '9' {
inputType = "number"
}
if inputType == "object" {
schema, requestMap = generatePropertiesForJsonObject(raw)
} else {
schema = &openapi3.Schema{
Type: inputType,
}
}
} else if ctType == "application/x-www-form-urlencoded" || ctType == "multipart/form-data" {
var values = raw.(url.Values)
inputType = "object"
schema, requestMap = generatePropertiesForValues(values)
} else {
logrus.Errorln("未知的请求类型", ctType)
goto handle
}
reqDef = &openapi3.MediaType{
Schema: &openapi3.SchemaRef{
Value: schema,
},
}
if method.RequestBody != nil && method.RequestBody.Value != nil {
if method.RequestBody.Value.Content == nil {
method.RequestBody.Value.Content = make(openapi3.Content)
}
method.RequestBody.Value.Content[mSel.RequestBody.Default] = reqDef
} else {
method.RequestBody = &openapi3.RequestBodyRef{
Value: &openapi3.RequestBody{
Content: openapi3.Content{
mSel.RequestBody.Default: reqDef,
},
},
}
}
hasChange = true
} else if len(method.RequestBody.Value.Content) == 1 {
var k string
for k, reqDef = range method.RequestBody.Value.Content {
mSel.RequestBody.Default = k
break
}
} else {
mSel.RequestBody.Default = args.RequestType
args.InputType = InputType_AsWholeRaw
}
} else {
fmt.Println("request body defined", reqDef)
}
if reqDef == nil {
err = errors.New(errors.ERR_NO_SUCH_SCHEMA, "没有找到需要的传输内容定义,或在多个定义中未指定选择项")
return
}
handle:
if requestMap == nil {
switch ctType {
case "application/json":
json.Unmarshal(args.RequestBody.([]byte), &requestMap)
case "application/x-www-form-urlencoded", "multipart/form-data":
requestMap = make(map[string]interface{})
for k, v := range args.RequestBody.(url.Values) {
requestMap[k] = v[0]
}
}
}
if args.InputType == InputType_AsStandard {
var handleMark = make(map[string]interface{})
var hasChangeSingle bool
if reqDef != nil && reqDef.Schema != nil && reqDef.Schema.Value != nil {
for _, schema := range reqDef.Schema.Value.AllOf {
hasChangeSingle, requestMap, _ = generateReqBySchema(handleMark, schema.Value.Properties, reqDef.Encoding, mSel, method.Parameters, requestMap, fn)
hasChange = hasChangeSingle || hasChange
}
hasChangeSingle, requestMap, _ = generateReqBySchema(handleMark, reqDef.Schema.Value.Properties, reqDef.Encoding, mSel, method.Parameters, requestMap, fn)
hasChange = hasChangeSingle || hasChange
rawRequest = requestMap
}
} else if args.InputType == InputType_AsRawMap {
rawRequest = args.Input
} else if args.InputType == InputType_AsWholeRaw {
rawRequest = args.RequestBody
}
// }
if reqDef != nil {
//根据定义重新组织请求,再提交
switch mSel.RequestBody.Default {
case "application/json":
requestBts, err = json.Marshal(rawRequest)
extraInfos.Request = json.RawMessage(requestBts)
if err != nil {
return
}
case "application/x-www-form-urlencoded":
//TODO 这种模式下没有处理使用raw的情况
var body = make(url.Values)
for k, v := range requestMap {
body.Set(k, fmt.Sprintf("%s", v))
}
requestBts = []byte(body.Encode())
extraInfos.Request = body.Encode()
case "multipart/form-data":
//TODO 这种模式下没有处理使用raw的情况
var body = &bytes.Buffer{}
var writer = multipart.NewWriter(body)
for k, v := range requestMap {
writer.WriteField(k, fmt.Sprintf("%s", v))
}
writer.Close()
requestBts = body.Bytes()
ctType = writer.FormDataContentType()
default:
extraInfos.Request = string(requestBts)
}
}
}
u.RawQuery = q.Encode()
// var body = null
//TODO 处理body信息
// axiosConfig.body = body
//设置返回类型
// if (method.response) {
// var response = t.components.responses[method.response.default];
// }
extraInfos.Url = u.String()
var req *http.Request
logrus.WithField("request url", u.String()).Info("request target")
req, err = http.NewRequest(strings.ToUpper(nameOfMethod), u.String(), bytes.NewBuffer(requestBts))
if err == nil {
var resp *http.Response
if mSel.RequestBody != nil && mSel.RequestBody.Default != "" {
req.Header.Set("Content-Type", ctType)
}
if args.Origin != "" {
req.Header.Set("Origin", args.Origin)
}
var client *http.Client
if args.DisableSslCheck {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client = &http.Client{Transport: tr}
} else {
client = http.DefaultClient
}
resp, err = client.Do(req)
if err == nil {
if args.ShowCertification {
if resp != nil &&
resp.TLS != nil &&
resp.TLS.PeerCertificates != nil &&
len(resp.TLS.PeerCertificates) > 0 {
extraInfos.X509 = resp.TLS.PeerCertificates[0]
}
}
extraInfos.Header = resp.Header
body = resp.Body
statuscode = resp.StatusCode
}
}
return
}
type DefHandler interface {
TransformValue(def *openapi3.Schema, value interface{}) (bool, interface{}, error)
TransformValueVersa(def *openapi3.Schema, value interface{}) (bool, interface{}, error)
CreateResponseIfNotDefined() bool
}
type TestResult struct {
Raw string `json:"raw"` //原始的返回值
Url string `json:"url"`
Header http.Header `json:"Header"`
Request interface{} `json:"request"`
Response json.RawMessage `json:"response"`
Def string `json:"def"`
Parsed interface{} `json:"parsed"`
X509 *x509.Certificate `json:"x509"`
Type string `json:"type"`
}
func TestResponse(method *openapi3.Operation, body io.ReadCloser, extraInfos *TestResult,
mSel *OperationSelection,
statuscode int,
fn DefHandler) (bool, error) {
bts, err := ioutil.ReadAll(body)
var hasChange bool
if err == nil {
extraInfos.Raw = string(bts)
var rawHeader = extraInfos.Header
//get the resposne def
var resDef *openapi3.ResponseRef
var ok bool
var createIfNotDefined = false
if mSel != nil && mSel.Response != nil && mSel.Response.Default != "" {
resDef, ok = method.Responses[mSel.Response.Default]
if !ok {
err = errors.New(errors.ERR_NO_SUCH_METHOD, "没有这个返回"+mSel.Response.Default)
}
} else if len(method.Responses) == 1 {
for _, res := range method.Responses {
resDef = res
ok = true
}
} else {
//尝试使用状态码匹配对应的输出
var sCode = strconv.Itoa(statuscode)
resDef, ok = method.Responses[sCode]
if !ok {
//有多个response,但是没有选择的情况
resDef, ok = method.Responses["default"]
if !ok {
if len(method.Responses) > 1 {
err = errors.New(errors.ERR_NO_SUCH_METHOD, "该方法有多个返回,没有指定返回类型")
} else {
createIfNotDefined = fn.CreateResponseIfNotDefined()
if createIfNotDefined {
if method.Responses == nil {
method.Responses = make(map[string]*openapi3.ResponseRef)
}
resDef = &openapi3.ResponseRef{
Value: &openapi3.Response{},
}
method.Responses["default"] = resDef
ok = true
} else {
err = errors.New(errors.ERROR_CALLING_NO_RESPONSE_DEFINED)
}
}
}
}
}
var testOtherRes string
var tested = make(map[string]bool)
testLoop:
for ok {
//TODO check header
//check body
var typ string
if rawHeader != nil {
typ = strings.Split(rawHeader.Get("Content-Type"), ";")[0] //get rid of the charset
}
var searched = typ
if typ == "text/html" {
//所有html的结构暂时采用application/json来进行解析
typ = "application/json"
extraInfos.Type = "json"
} else if typ == "text/xml" {
typ = "application/xml"
extraInfos.Type = "xml"
} else if typ == "application/json" {
extraInfos.Type = "json"
} else {
extraInfos.Type = "raw"
}
var def *openapi3.MediaType
if createIfNotDefined {
var schema = &openapi3.Schema{}
if len(bts) > 0 {
if bts[0] == '{' {
schema.Type = "object"
} else if bts[0] == '[' {
schema.Type = "array"
} else if bts[0] == '"' {
schema.Type = "string"
} else if bts[0] == 't' {
schema.Type = "boolean"
} else if bts[0] == 'f' {
schema.Type = "boolean"
} else if bts[0] == 'n' {
schema.Type = "null"
} else if bts[0] == '-' {
schema.Type = "number"
} else if bts[0] >= '0' && bts[0] <= '9' {
schema.Type = "number"
}
}
resDef.Value.Content = make(map[string]*openapi3.MediaType)
def = &openapi3.MediaType{
Schema: &openapi3.SchemaRef{
Value: schema,
},
}
resDef.Value.Content[typ] = def
} else {
def = resDef.Value.Content.Get(typ)
}
if def == nil {
searched += ";*/*"
def = resDef.Value.Content.Get("*/*")
}
if def != nil {
tested[typ] = true
if typ == "application/json" {
//尝试对数据进行解码
var data interface{}
er := json.Unmarshal(bts, &data)
if er == nil {
extraInfos.Response = json.RawMessage(bts)
}
}
if err = def.Validate(context.Background()); err != nil {
err = errors.New(errors.ERR_VALIDATE_SCHEMA_ERROR, err.Error())
} else if def.Schema == nil {
// 没有定义返回类型的情况
} else if def.Schema.Value == nil {
err = errors.New(errors.ERR_NO_SUCH_SCHEMA)
bts, _ := def.Schema.MarshalJSON()
extraInfos.Def = string(bts)
} else {
var res interface{}
hasChange, res, err = TestSchemaInResponse(typ, def.Schema.Value, bts, fn)
if errors.Code(err) == errors.ERROR_CALLING_NOT_CORRECT_RESPONSE {
if len(method.Responses) > 1 {
for k, v := range method.Responses {
_, done := tested[k]
if k != mSel.Response.Default && !done {
resDef = v
testOtherRes = k
tested[k] = true
continue testLoop
}
}
}
} else if err == nil {
extraInfos.Parsed = res
}
}
} else {
err = fmt.Errorf("没有找到合适的返回类型,返回%s,寻找%s,", typ, searched)
}
break
}
if err == nil {
if mSel.Response != nil && testOtherRes != mSel.Response.Default {
err = errors.New(errors.ERROR_CALLING_NOT_CORRECT_RESPONSE, fmt.Sprintf("期望获得%s类型回复,收到%s", mSel.Response.Default, testOtherRes))
}
}
hasChange = hasChange || createIfNotDefined
}
return hasChange, err
}
//对返回结果进行测试
func TestSchemaInResponse(typ string, schema *openapi3.Schema, bts []byte, contentTester DefHandler) (hasChange bool, obj interface{}, err error) {
if schema.AllOf != nil {
for _, v := range schema.AllOf {
hasChange, _, err = TestSchemaInResponse(typ, v.Value, bts, contentTester)
if err != nil {
return hasChange, nil, errors.New(errors.ERROR_CALLING_NOT_CORRECT_RESPONSE, err)
}
}
return hasChange, nil, nil
}
switch schema.Type {
case "object":
obj = new(DataResult)
case "string":
var str string
obj = &str
case "array":
var arr []interface{}
obj = &arr
case "number":
var num float64
obj = &num
case "integer":
var num int64
obj = &num
case "boolean":
var b bool
obj = &b
}
switch typ {
case "application/json":
err = json.Unmarshal(bts, obj) //DataResult实际是将json转成了map
case "application/xml":
err = xml.Unmarshal(bts, obj)
}
if err != nil {
return
}
switch schema.Type {
case "object":
var updated openapi3.Schemas
hasChange, updated, err = checkForObject(obj.(*DataResult).Values, schema.Properties, contentTester)
if hasChange {
schema.Properties = updated
}
}
return
}
func checkForObject(mp map[string]interface{}, schemas openapi3.Schemas, contentTester DefHandler) (bool, openapi3.Schemas, error) {
var updated = false
var updatedSchemas = schemas
for k, v := range mp {
schema, ok := schemas[k]
if !ok {
if contentTester.CreateResponseIfNotDefined() {
var typ string
switch v.(type) {
case string:
typ = "string"
case float64:
typ = "number"
case int64:
typ = "integer"
case bool:
typ = "boolean"
case map[string]interface{}:
typ = "object"
case []interface{}:
typ = "array"
}
if updatedSchemas == nil {
updatedSchemas = make(openapi3.Schemas)
}
schema = &openapi3.SchemaRef{
Value: &openapi3.Schema{
Type: typ,
},
}
updatedSchemas[k] = schema
} else {
return false, updatedSchemas, errors.New(errors.ERROR_CALLING_NOT_CORRECT_RESPONSE, fmt.Sprintf("参数(%s)返回结构不符合定义,需要%s,实际是%T", k, schema.Value.Type, v))
}
}
if schema.Value.Type == "object" {
mv, ok := v.(map[string]interface{})
if !ok {
return false, updatedSchemas, errors.New(errors.ERROR_CALLING_NOT_CORRECT_RESPONSE, fmt.Sprintf("参数(%s)返回结构不符合定义,需要%s,实际是%T", k, schema.Value.Type, v))
}
updatedSingle, result, err := checkForObject(mv, schema.Value.Properties, contentTester)
if err != nil {
return updated, updatedSchemas, err
} else {
updated = updated || updatedSingle
if updatedSingle {
schema.Value.Properties = result
}
}
} else if contentTester != nil {
//非Object的普通字段
//TODO 先假设所有字段都提前配置好,不存在没有配置的情况
updatedSingle, newV, err := contentTester.TransformValue(schema.Value, v)
if err != nil {
return updatedSingle, updatedSchemas, err
} else {
mp[k] = newV
updated = updated || updatedSingle
}
}
}
for k, v := range schemas {
if _, ok := mp[k]; !ok && isRequired(v.Value.Required, k) {
return false, updatedSchemas, errors.New(errors.ERROR_CALLING_NOT_CORRECT_RESPONSE, fmt.Sprintf("没有找到需要的参数(%s)", k))
}
}
return updated, updatedSchemas, nil
}
func isRequired(required []string, key string) bool {
for _, v := range required {
if v == key {
return true
}
}
return false
}
func generatePropertiesForValues(values url.Values) (schema *openapi3.Schema, mp map[string]interface{}) {
if values == nil {
return
}
mp = make(map[string]interface{})
schema = &openapi3.Schema{
Type: "object",
}
schema.Properties = make(openapi3.Schemas)
var typ string
for k, v := range values {
if len(v) > 0 {
mp[k] = v[0]
if !strings.Contains(v[0], ".") && len(v[0]) > 6 {
//对于长度大于6的字符串,不考虑是数字或者浮点数,直接当做字符串,避免误判
typ = "string"
} else if iV, err := strconv.ParseInt(v[0], 10, 64); err == nil {
mp[k] = iV
typ = "integer"
} else if fV, err := strconv.ParseFloat(v[0], 64); err == nil {
mp[k] = fV
typ = "number"
} else if bV, err := strconv.ParseBool(v[0]); err == nil {
mp[k] = bV
typ = "boolean"
} else {
typ = "string"
}
schema.Properties[k] = &openapi3.SchemaRef{
Value: &openapi3.Schema{
Type: typ,
},
}
}
}
return
}
func generatePropertiesForJsonObject(bts interface{}) (schema *openapi3.Schema, mp map[string]interface{}) {
var err error
if bts == nil {
return
}
switch bts.(type) {
case []byte:
err = json.Unmarshal(bts.([]byte), &mp)
case map[string]interface{}:
mp = bts.(map[string]interface{})
}
schema = &openapi3.Schema{
Type: "object",
}
if err == nil {
schema.Properties = make(openapi3.Schemas)
for k, v := range mp {
var typ string
var elem interface{}
switch tv := v.(type) {
case string:
typ = "string"
case float64:
typ = "number"
case int64:
typ = "integer"
case bool:
typ = "boolean"
case map[string]interface{}:
typ = "object"
elem = tv
case []interface{}:
typ = "array"
if len(tv) > 0 {
elem = tv[0]
}
}
var child *openapi3.Schema
if typ == "object" {
child, _ = generatePropertiesForJsonObject(elem)
} else if typ == "array" {
child = &openapi3.Schema{
Type: typ,
}
children, _ := generatePropertiesForJsonObject(elem)
child.Items = &openapi3.SchemaRef{
Value: children,
}
} else {
child = &openapi3.Schema{
Type: typ,
}
}
schema.Properties[k] = &openapi3.SchemaRef{
Value: child,
}
}
}
return
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。