Обновлен API погоды Yahoo - .NET - PullRequest
0 голосов
/ 22 января 2019

Я пытаюсь реализовать новейший API погоды yahoo в приложении .NET (используемое нами было прекращено в пользу этого нового): https://developer.yahoo.com/weather/documentation.html#commercial

Их единственные примеры на PHPи Java.Я приложил все усилия, чтобы преобразовать пример Java в .NET, но я все еще получаю ответ «401 - Несанкционированный».Я просмотрел свой код несколько раз и не могу найти никаких проблем, поэтому я надеюсь, что кто-то еще сможет увидеть, где я ошибся.Код ниже.

    Private Sub _WeatherLoader_DoWork(sender As Object, e As DoWorkEventArgs)

        Try

            Dim oauth As New OAuth.OAuthBase
            Dim forecastRssResponse As query
            Dim appId As String = My.Settings.YahooAppID
            Dim consumerKey As String = My.Settings.YahooAPIConsumerKey
            Dim yahooUri As String = String.Format("{0}?location=billings,mt&format=xml", YAHOO_WEATHER_API_BASE_ENDPOINT)
            Dim oAuthTimestamp As Integer = oauth.GenerateTimeStamp()
            Dim oAuthNonce As String = oauth.GenerateNonce()
            Dim parameters As New List(Of String)

            Try

                parameters.Add(String.Format("oauth_consumer_key={0}", consumerKey))
                parameters.Add(String.Format("oauth_nonce={0}", oAuthNonce))
                parameters.Add("oauth_signature_method=HMAC-SHA1")
                parameters.Add(String.Format("oauth_timestamp={0}", oAuthTimestamp.ToString()))
                parameters.Add("oauth_version=1.0")

                ' Encode the location
                parameters.Add(String.Format("location={0}", HttpUtility.UrlEncode("billings,mt", Encoding.UTF8)))
                parameters.Add("format=xml")

                ' Sort parameters ascending
                parameters = parameters.OrderBy(Function(item) item).ToList()

                Dim i As Integer = 0
                Dim builder As New StringBuilder()

                Do While (i < parameters.Count())

                    builder.Append(String.Format("{0}{1}", If(i > 0, "&", String.Empty), parameters(i)))
                    i += 1

                Loop

                Dim signatureString As String = String.Format("GET&{0}&{1}", HttpUtility.UrlEncode(YAHOO_WEATHER_API_BASE_ENDPOINT, Encoding.UTF8), HttpUtility.UrlEncode(builder.ToString(), Encoding.UTF8))
                Dim oAuthSignature As String = _CreateOauthSignature(signatureString)
                Dim authorizationLine As String = String.Format("OAuth oauth_consumer_key={0}, oauth_nonce={1}, oauth_timestamp={2}, oauth_signature_method=HMAC-SHA1, oauth_signature={3}, oauth_version=1.0", consumerKey, oAuthNonce, oAuthTimestamp, oAuthSignature)
                Dim forecastRequest As WebRequest = WebRequest.Create(yahooUri)

                forecastRequest.Headers.Add("Authorization", authorizationLine)
                forecastRequest.Headers.Add("Yahoo-App-Id", appId)

                ' Cast to HttpWebRequest to set ContentType through property
                CType(forecastRequest, HttpWebRequest).ContentType = "text/xml"

                Dim forecastResponse As WebResponse = forecastRequest.GetResponse()

                If forecastResponse IsNot Nothing Then

                    Using responseStream As Stream = forecastResponse.GetResponseStream()

                        Dim rssDoc As New XmlDocument()

                        rssDoc.Load(responseStream)
                        forecastRssResponse = rssDoc.OuterXml().FromXml(Of query)()

                    End Using

                    e.Result = forecastRssResponse

                End If

            Catch ex As Exception

                e.Result = Nothing
                LoadingManually = False

            End Try

        Catch ex As Exception
            modMain.SendDevErrorEmail(ex, "_WeatherLoader_DoWork in WeatherWidget", "Catch around dowork code in fired from refresh timer event in wether widget")

            e.Result = Nothing
            LoadingManually = False
        End Try

    End Sub

Private Function _CreateOauthSignature(baseInfo As String) As String

    Dim secretKey As String = String.Format("{0}&", My.Settings.YahooAPIConsumerSecretKey)
    Dim encoding As New System.Text.ASCIIEncoding()
    Dim keyBytes As Byte() = encoding.GetBytes(secretKey)
    Dim messageBytes As Byte() = encoding.GetBytes(baseInfo)
    Dim hashMessage As Byte()

    Using hmac As New HMACSHA1(keyBytes)

    hashMessage = hmac.ComputeHash(messageBytes)

    End Using

    Return Convert.ToBase64String(hashMessage)

End Function

1 Ответ

0 голосов
/ 23 января 2019

После кропотливого создания приложения Java, вставки в пример Java и перехода по нему я обнаружил, что проблема заключается в плохо реализованной функции декодирования URL на принимающей стороне.

В приложении Java URL Encode использует символы верхнего регистра, тогда как в .NET HTTPUtility.URLEncode использует символы нижнего регистра. Этого достаточно, чтобы скинуть вашу подпись и вызвать 401 - Несанкционированная ошибка.

Моим решением было создать метод расширения строки, который будет кодировать URL в верхнем регистре:

  <Extension>
    Public Function UppercaseURLEncode(ByVal sourceString As String) As String

        Dim temp As Char() = HttpUtility.UrlEncode(sourceString).ToCharArray()

        For i As Integer = 0 To temp.Length - 2

            If temp(i).ToString().Equals("%", StringComparison.OrdinalIgnoreCase) Then

                temp(i + 1) = Char.ToUpper(temp(i + 1))
                temp(i + 2) = Char.ToUpper(temp(i + 2))

            End If

        Next

        Return New String(temp)

    End Function

Используя этот метод расширения, моя подпись создается точно так же, как и в приложении Java, и я могу получить ответ.

Надеюсь, что это поможет другим программистам .net с этой проблемой!

...