这个问题是由错别字或无法再复制的问题引起的。虽然类似的问题在这里可能是主题,但这个问题的解决方式不太可能帮助未来的读者。
我正在尝试对AlphaVantage的损益表endpoint进行API调用,然后将响应散集到我的IncomStmt结构中。它没有引发任何类型的错误,但是当我尝试打印结果时,我得到了
这是endpoint:https://www.alphavantage.co/query?function=INCOME_STATEMENT
下面是API响应结构:https://www.alphavantage.co/query?function=INCOME_STATEMENT
到目前为止,我已经梳理了我的IncomStmt
、QuarterlyReport
和AnnualReport
结构,所有字段都与API中的名称和类型(所有字符串都符合上面API结构)相匹配。我检查了我是否正确导出了所有字段,我相信我的getJson
函数工作正常,因为我已经能够使用该函数散集一个更简单的API响应。所以,我认为问题在于我如何创建要散集的结构。
我的预期结果是在GetIncomStmt
的fmt. Printf("UnmarshaledIncomStmt:%v\n",incomStmt)
中看到打印出来的API响应。
下面的代码:
type AnnualReports struct {
FiscalDateEnding string `json:"fiscalDateEnding"`
ReportedCurrency string `json:"reportedCurrency"`
GrossProfit string `json:"grossProfit"`
TotalRevenue string `json:"totalRevenue"`
CostOfRevenue string `json:"costOfRevenue"`
CostOfGoodsAndServicesSold string `json:"costOfGoodsAndServicesSold"`
OperatingIncome string `json:"operatingIncome"`
SellingGeneralAndAdministrative string `json:"sellingGeneralAndAdministrative"`
ResearchAndDevelopment string `json:"researchAndDevelopment"`
OperatingExpenses string `json:"operatingExpenses"`
InvestmentIncomeNet string `json:"investmentIncomeNet"`
NetInterestIncome string `json:"netInterestIncome"`
InterestIncome string `json:"interestIncome"`
InterestExpense string `json:"interestExpense"`
NonInterestIncome string `json:"nonInterestIncome"`
OtherNonOperatingIncome string `json:"otherNonOperatingIncome"`
Depreciation string `json:"depreciation"`
DepreciationAndAmortization string `json:"depreciationAndAmortization"`
IncomeBeforeTax string `json:"incomeBeforeTax"`
IncomeTaxExpense string `json:"incomeTaxExpense"`
InterestAndDebtExpense string `json:"interestAndDebtExpense"`
NetIncomeFromContinuingOperations string `json:"netIncomeFromContinuingOperations"`
ComprehensiveIncomeNetOfTax string `json:"comprehensiveIncomeNetOfTax"`
Ebit string `json:"ebit"`
Ebitda string `json:"ebitda"`
NetIncome string `json:"netIncome"`
}
type QuarterlyReports struct {
FiscalDateEnding string `json:"fiscalDateEnding"`
ReportedCurrency string `json:"reportedCurrency"`
GrossProfit string `json:"grossProfit"`
TotalRevenue string `json:"totalRevenue"`
CostOfRevenue string `json:"costOfRevenue"`
CostOfGoodsAndServicesSold string `json:"costOfGoodsAndServicesSold"`
OperatingIncome string `json:"operatingIncome"`
SellingGeneralAndAdministrative string `json:"sellingGeneralAndAdministrative"`
ResearchAndDevelopment string `json:"researchAndDevelopment"`
OperatingExpenses string `json:"operatingExpenses"`
InvestmentIncomeNet string `json:"investmentIncomeNet"`
NetInterestIncome string `json:"netInterestIncome"`
InterestIncome string `json:"interestIncome"`
InterestExpense string `json:"interestExpense"`
NonInterestIncome string `json:"nonInterestIncome"`
OtherNonOperatingIncome string `json:"otherNonOperatingIncome"`
Depreciation string `json:"depreciation"`
DepreciationAndAmortization string `json:"depreciationAndAmortization"`
IncomeBeforeTax string `json:"incomeBeforeTax"`
IncomeTaxExpense string `json:"incomeTaxExpense"`
InterestAndDebtExpense string `json:"interestAndDebtExpense"`
NetIncomeFromContinuingOperations string `json:"netIncomeFromContinuingOperations"`
ComprehensiveIncomeNetOfTax string `json:"comprehensiveIncomeNetOfTax"`
Ebit string `json:"ebit"`
Ebitda string `json:"ebitda"`
NetIncome string `json:"netIncome"`
}
type IncomeStmt struct {
Symbol string `json:"symbol"`
AnnualReports []AnnualReports `json:"annualReports"`
QuarterlyReports []QuarterlyReports `json:"quarterlyReports"`
}
type Client interface {
GetEarnings(symbol string) (*Earnings, error)
GetIncomeStmt(symbol string) (*IncomeStmt, error)
}
type ClientOpts struct {
ApiKey string
BaseUrl string
HttpClient *http.Client
}
type client struct {
opts ClientOpts
httpClient *http.Client
}
func NewClient(opts ClientOpts) Client {
if opts.ApiKey == "" {
opts.ApiKey = secretProvider.GetEnv("ALPHA_VANTAGE_API_KEY")
}
if opts.BaseUrl == "" {
opts.BaseUrl = secretProvider.GetEnv("ALPHA_VANTAGE_API_BASE_URL")
}
httpClient := opts.HttpClient
if httpClient == nil {
httpClient = &http.Client{Timeout: 10 * time.Second}
}
return &client{
opts: opts,
httpClient: httpClient,
}
}
func (c *client) get(u *url.URL) (*http.Response, error) {
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
// getJson will take the http response and unmarshal it, returning an error if unsuccessful
func getJson(resp *http.Response, data interface{}) error {
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(&data)
}
func (c *client) GetIncomeStmt(symbol string) (*IncomeStmt, error) {
u, err := url.Parse(fmt.Sprintf("%s?function=INCOME_STATEMENT&symbol=%s&apiKey=%s", c.opts.BaseUrl, symbol, c.opts.ApiKey))
if err != nil {
return nil, err
}
resp, err := c.get(u)
if err != nil {
return nil, err
}
incomeStmt := &IncomeStmt{}
if err = getJson(resp, incomeStmt); err != nil {
fmt.Print(err)
log.Fatal("Error decoding data in getJson fn.")
return nil, err
}
fmt.Printf("Unmarshaled IncomeStmt: %v \n", incomeStmt)
return incomeStmt, nil
}
这里的问题是我在endpointURL中错误地camelCase了apiKey
。这样做会导致我的API密钥没有在GET请求中传递给AlphaVantage。我发现这要归功于Cerise上面的评论。当API返回200状态时(即使我的请求格式错误!),它促使我尝试使用ioutil. ReadAll()
以不同的方式读取响应正文。需要注意的是,根据这篇文章,ReadAll()
方法在实际使用中是不执行的。我仅将以下内容用于调试目的:
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error using readall")
}
fmt.Println(string(body))
它打印了一直隐藏的错误消息:
{
"Error Message": "the parameter apikey is invalid or missing. Please claim your free API key on (https://www.alphavantage.co/support/#api-key). It should take less than 20 seconds."
}
将查询参数apiKey
降低到apikey
导致我得到了预期的响应。这个故事的寓意是我需要添加额外的错误处理,以防API没有返回预期的响应体,这样程序就不会静默失败。