一个简易的SpringCloud Http Post 请求发送工具

       为了方便开发同学,用Python写了一个POST请求发送可执行脚本,方便那些不能使用postman等使用的局域网。当然可以使用CURL命令、用PHP写一个也未尝不可,但并不具备跨平台性。

当然也可以使用go进行开发。废话不多说,show me code。

原理:

获取到 Linux 系统配置的 apollo 配置中心地址:http://apolloUrl/

通过访问apollo配置中心  http://apolloUrl/configs/公共配置ID/default/公共配置namespace,获取到eureka的服务器地址。

然后访问 http://eurekaUrl/eureka/apps/ 获取到注册应用的真实ip地址。

解析XML配置,最后替换并访问正确的目标地址。

import json
import os
import re
import sys

import requests
from xml.dom.minidom import parseString
from sys import argv

# 公共配置appId
PUBLIC_APOLLO_CONFIG_APP_ID = "xxxx.public"
# 公共配置namepsace
PUBLIC_APOLLO_CONFIG_NAMESPACE = "PUBLIC"
# 公共配置key
EUREKA_URL_KEY = "eureka.service.url"

JSON_HEADERS = {
    'content-type': "application/json;charset=UTF-8",
    'user-agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
}


def doPost(url, postData):
    # 提交post请求
    response = requests.post(url, headers=JSON_HEADERS, data=postData)
    return response


def doGet(url):
    # 提交post请求
    response = requests.get(url, headers=JSON_HEADERS)
    return response.text


def isIpAddressPort(ipStr):
    p = re.compile('^((25[0-5]|2[0-4]d|[01]?dd?).){3}(25[0-5]|2[0-4]d|[01]?dd?)(:d+)$')
    if p.match(ipStr):
        return True
    else:
        return False


def parseEurekaUrl(confgJsonStr):
    data = json.loads(confgJsonStr)
    eurekaUrlStr = data['configurations'][EUREKA_URL_KEY]
    eurekaUrls = eurekaUrlStr.split(',')
    return eurekaUrls[0]

# python解析XML: https://www.cnblogs.com/gide/p/12155821.html
def getRealIpPort(configXml, appNamStr):
    # 读取文件
    dom = parseString(configXml)
    # 获取文档元素对象
    data = dom.documentElement
    # 获取 student
    applications = data.getElementsByTagName('application')
    for appInfo in applications:
        # 获取标签中内容
        appName = appInfo.getElementsByTagName('name')[0].childNodes[0].nodeValue
        if appName != appNamStr:
            continue
        # 获取标签中内容
        return appInfo.getElementsByTagName('instanceId')[0].childNodes[0].nodeValue


def getRealHttpUrl(apolloUrl, httpUrl):
    httpUrlTmp = httpUrl.replace("http://", '')
    httpUrlTmp = httpUrlTmp.replace("https://", '')
    urls = httpUrlTmp.split("/")
    appName = urls[0].upper()

    if isIpAddressPort(appName):
        return httpUrl

    endStr = apolloUrl[-1]
    if "/" == endStr:
        apolloUrl = apolloUrl[0:-1]

    url = apolloUrl + "/configs/" + PUBLIC_APOLLO_CONFIG_APP_ID + "/default/" + PUBLIC_APOLLO_CONFIG_NAMESPACE
    eurekaUrl = parseEurekaUrl(doGet(url))
    endStr = eurekaUrl[-1]
    if "/" == endStr:
        eurekaUrl = eurekaUrl[0:-1]
    eurekaUrl += "/apps"

    realIpPort = getRealIpPort(doGet(eurekaUrl), appName)
    return httpUrl.replace(appName, realIpPort)


def callService(apolloUrl, httpUrl, postData):
    try:
        httpUrlOrigin = httpUrl
        httpUrl = getRealHttpUrl(apolloUrl=apolloUrl, httpUrl=httpUrl)

        print("
RequestOriginUrl: {}".format(httpUrlOrigin))
        print("RequestMethod: POST")
        print("RequestUrl: {}".format(httpUrl))
        print("RequestData:")
        print("{}".format(json.dumps(json.loads(postData), indent=4)))
        response = json.loads(doPost(httpUrl, postData).text)
        print("ResponseDump:")
        print("{}
".format(json.dumps(response, indent=4)))
        print("Response:")
        print("{}
".format(json.dumps(response)))
    except Exception as e:
        print("error:{}".format(repr(e)))


if __name__ == '__main__':
    apolloUrl = os.getenv('APOLLO_META')
    if 0 == len(apolloUrl):
        print("Please Set the APOLLO_META variable in the /etc/profile file !")
        sys.exit(1)

    if len(argv) < 3:
        print("Please input URL and PostData !")
        print(
            "Simple: ./springTest http://ABC/cde/fgh '{"a":"b"}' Or ./springTest http://ip[:port]/cde/fgh '{"a":"b"}'")
        sys.exit(1)

    callService(apolloUrl, argv[1], argv[2]) 

编译成可执行文件:

pip3 install pyinstaller
pyinstaller -F springTest.py

使用示例:

./springTest http://应用id/xxxCtl/xxxAction '{"a":"b"}'

./springTest http://ip[:port]/xxxCtl/xxxAction '{"a":"b"}'

Golang版本:

package main

import (
    "log"
    "fmt"
    "encoding/json"
    "encoding/xml"
    "strings"
    "regexp"
    "bytes"
    "os"
    "net/http"
    "io/ioutil"
)

//  https://mholt.github.io/json-to-go/
type PublicConfig struct {
    AppID         string `json:"appId"`
    Cluster       string `json:"cluster"`
    NamespaceName string `json:"namespaceName"`
    Configurations struct {
        ManagementEndpointsWebExposureInclude string `json:"management.endpoints.web.exposure.include"`
        EurekaInstanceInstanceID              string `json:"eureka.instance.instance-id"`
        EurekaClientRegisterWithEureka        string `json:"eureka.client.register-with-eureka"`
        InfoAppName                           string `json:"info.app.name"`
        ManagementEndpointMetricsEnabled      string `json:"management.endpoint.metrics.enabled"`
        EurekaInstanceStatusPageURL           string `json:"eureka.instance.status-page-url"`
        EurekaInstanceMetadataMapAppVersion   string `json:"eureka.instance.metadata-map.app_version"`
        EurekaInstancePreferIPAddress         string `json:"eureka.instance.prefer-ip-address"`
        EurekaClientFetchRegistry             string `json:"eureka.client.fetch-registry"`
        ManagementEndpointHealthShowDetails   string `json:"management.endpoint.health.show-details"`
        EurekaServiceURL                      string `json:"eureka.service.url"`
        EurekaClientServiceURLDefaultZone     string `json:"eureka.client.serviceUrl.defaultZone"`
    } `json:"configurations"`
    ReleaseKey string `json:"releaseKey"`
}

//  https://www.onlinetool.io/xmltogo/
type SpringApplicationConfig struct {
    XMLName       xml.Name `xml:"applications"`
    VersionsDelta string   `xml:"versions__delta"`
    AppsHashcode  string   `xml:"apps__hashcode"`
    Application []struct {
        Name string `xml:"name"`
        Instance struct {
            InstanceId       string `xml:"instanceId"`
            HostName         string `xml:"hostName"`
            App              string `xml:"app"`
            IpAddr           string `xml:"ipAddr"`
            Status           string `xml:"status"`
            Overriddenstatus string `xml:"overriddenstatus"`
            Port struct {
                Text    string `xml:",chardata"`
                Enabled string `xml:"enabled,attr"`
            } `xml:"port"`
            SecurePort struct {
                Text    string `xml:",chardata"`
                Enabled string `xml:"enabled,attr"`
            } `xml:"securePort"`
            CountryId string `xml:"countryId"`
            DataCenterInfo struct {
                Text  string `xml:",chardata"`
                Class string `xml:"class,attr"`
                Name  string `xml:"name"`
            } `xml:"dataCenterInfo"`
            LeaseInfo struct {
                Text                  string `xml:",chardata"`
                RenewalIntervalInSecs string `xml:"renewalIntervalInSecs"`
                DurationInSecs        string `xml:"durationInSecs"`
                RegistrationTimestamp string `xml:"registrationTimestamp"`
                LastRenewalTimestamp  string `xml:"lastRenewalTimestamp"`
                EvictionTimestamp     string `xml:"evictionTimestamp"`
                ServiceUpTimestamp    string `xml:"serviceUpTimestamp"`
            } `xml:"leaseInfo"`
            Metadata struct {
                Text           string `xml:",chardata"`
                Class          string `xml:"class,attr"`
                ManagementPort string `xml:"management.port"`
                InstanceId     string `xml:"instanceId"`
            } `xml:"metadata"`
            HomePageUrl                   string `xml:"homePageUrl"`
            StatusPageUrl                 string `xml:"statusPageUrl"`
            HealthCheckUrl                string `xml:"healthCheckUrl"`
            VipAddress                    string `xml:"vipAddress"`
            SecureVipAddress              string `xml:"secureVipAddress"`
            IsCoordinatingDiscoveryServer string `xml:"isCoordinatingDiscoveryServer"`
            LastUpdatedTimestamp          string `xml:"lastUpdatedTimestamp"`
            LastDirtyTimestamp            string `xml:"lastDirtyTimestamp"`
            ActionType                    string `xml:"actionType"`
            AppGroupName                  string `xml:"appGroupName"`
        } `xml:"instance"`
    } `xml:"application"`
}

// 公共配置appId
const PUBLIC_APOLLO_CONFIG_APP_ID = "xxxx.public"

// 公共配置namepsace
const PUBLIC_APOLLO_CONFIG_NAMESPACE = "PUBLIC"

// 公共配置key
const EUREKA_URL_KEY = "eureka.service.url"

func doPost(url string, postData string) string {
    client := &http.Client{}
    reqBytes := bytes.NewBuffer([]byte(postData))
    request, _ := http.NewRequest("POST", url, reqBytes)
    request.Header.Set("Connection", "keep-alive")
    request.Header.Set("Content-type", "application/json;charset=UTF-8")
    request.Header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36")
    response, _ := client.Do(request)
    if response.StatusCode != 200 {
        return ""
    }

    body, _ := ioutil.ReadAll(response.Body)
    return string(body);
}

func doGet(url string) string {
    // 提交get请求
    client := &http.Client{}
    request, _ := http.NewRequest("GET", url, nil)
    request.Header.Set("Connection", "keep-alive")
    response, _ := client.Do(request)
    if response.StatusCode != 200 {
        return ""
    }

    body, _ := ioutil.ReadAll(response.Body)
    return string(body);
}

func parseEurekaUrl(apolloUrl string) string {
    data := doGet(apolloUrl)

    config := PublicConfig{}

    err := json.Unmarshal([]byte(data), &config)
    //解析失败会报错,如json字符串格式不对,缺"号,缺}等。
    if err != nil {
        fmt.Println(err)
    }
    eurekaUrlStr := config.Configurations.EurekaServiceURL;
    eurekaUrls := strings.Split(eurekaUrlStr, ",");
    eurekaUrl := eurekaUrls[0]

    endStr := eurekaUrl[len(eurekaUrl)-1:]
    if "/" == endStr {
        eurekaUrl = eurekaUrl[0 : len(eurekaUrl)-1]
    }
    return eurekaUrl + "/apps";
}

func jsonStr2Map(jsonStr string) map[string]interface{} {
    var mapResult map[string]interface{}
    //使用 json.Unmarshal(data []byte, v interface{})进行转换,返回 error 信息
    if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
        log.Fatal(err)
        return nil
    }

    return mapResult
}

func dumJsonStr(jsonStr string) {
    var out bytes.Buffer
    err := json.Indent(&out, []byte(jsonStr), "", "	")

    if err != nil {
        log.Fatalln(err)
    }

    out.WriteTo(os.Stdout)
}

func getRealIpPort(eurekaUrl string, appNamStr string) string {
    dataXml := doGet(eurekaUrl)
    springAppConfig := SpringApplicationConfig{}
    err := xml.Unmarshal([]byte(dataXml), &springAppConfig)
    if err != nil {
        fmt.Printf("error: %v", err)
        return ""
    }

    applications := springAppConfig.Application;
    var realIpPort = ""
    for _, value := range applications {
        if appNamStr == value.Name {
            realIpPort = value.Instance.InstanceId;
            break;
        }
    }

    return realIpPort
}

func getRealHttpUrl(apolloUrl string, httpUrl string) string {
    httpUrlTmp := strings.Replace(httpUrl, "http://", "", -1)
    httpUrlTmp = strings.Replace(httpUrlTmp, "https://", "", -1)
    urls := strings.Split(httpUrlTmp, "/")
    appName := strings.ToUpper(urls[0])

    if isIpAddressPort(appName) {
        return httpUrl
    }

    endStr := apolloUrl[len(apolloUrl)-1:]
    if "/" == endStr {
        apolloUrl = apolloUrl[0 : len(apolloUrl)-1]
    }

    url := apolloUrl + "/configs/" + PUBLIC_APOLLO_CONFIG_APP_ID + "/default/" + PUBLIC_APOLLO_CONFIG_NAMESPACE

    eurekaUrl := parseEurekaUrl(url)
    realIpPort := getRealIpPort(eurekaUrl, appName)
    return strings.Replace(httpUrl, appName, realIpPort, -1)
}

func isIpAddressPort(ipStr string) bool {
    if isOk, _ := regexp.MatchString(`^((25[0-5]|2[0-4]d|[01]?dd?).){3}(25[0-5]|2[0-4]d|[01]?dd?)(:d+)$`, ipStr); isOk {
        return isOk
    }
    return false
}

func callService(apolloUrl string, httpUrl string, postData string) {
    httpUrlOrigin := httpUrl
    httpUrl = getRealHttpUrl(apolloUrl, httpUrl)
    fmt.Println("
RequestOriginUrl: " + httpUrlOrigin)
    fmt.Println("RequestMethod: POST")
    fmt.Println("RequestUrl: " + httpUrl)
    fmt.Println("RequestData:" + postData)
    response := doPost(httpUrl, postData)
    fmt.Println("
ResponseDump:")
    dumJsonStr(response)
    fmt.Println("
")
    fmt.Println("Response:")
    fmt.Println(response)
    fmt.Println("
")
}

func main() {
    apolloUrl := os.Getenv("APOLLO_META")
    if 0 == len(apolloUrl) {
        fmt.Println("Please Set the APOLLO_META variable in the /etc/profile file !")
        os.Exit(1)
    }

    if len(os.Args) < 3 {
        fmt.Println("Simple: ./springTest http://ABC/cde/fgh '{"a":"b"}' Or ./springTest http://ip[:port]/cde/fgh '{"a":"b"}'")
        os.Exit(1)
    }

    callService(apolloUrl, os.Args[1], os.Args[2])
}

编译:

go build springTest.go

在当前目录下会生成一个springTest可执行文件。

Shell简易版本,不支持SpringCloud:

#!/bin/bash

URL=$1
DATA_JSON=$2

if [ "${URL}"x == ""x ];then
    echo "第一个参数为URL,第二个参数为json字符串";
    echo "例如: ./[self].sh http://www.baidu.com '{"a":"b"}'"
    exit 0
fi

if [ "${DATA_JSON}"x == ""x ];then
    echo "第二个参数为json字符串不能为空";
    exit 0
fi


CURL_CMD="curl -H "Content-Type:application/json" -X POST --data '${DATA_JSON}' ${URL}"

echo ""
echo "${CURL_CMD}"
echo "========================================================================================="
echo ""
eval ${CURL_CMD}
echo ""
echo ""

PS:

编译时报依赖库不存在解决办法:http://www.pythonheidong.com/blog/article/158136/

原文地址:https://www.cnblogs.com/phpdragon/p/12551992.html