【问题标题】:Google Geocode API returns no response in SQL Server but valid XML response in browser for valid addressGoogle Geocode API 在 SQL Server 中不返回响应,但在浏览器中返回有效地址的有效 XML 响应
【发布时间】:2020-10-12 18:03:35
【问题描述】:

我有这个地址 3926 Linden St, Bethlehem, PA 18020 使用 SQL Server 2016 中的 Google Geocode API 进行地理编码。这是一个有效的地址,我用来打开 HTTP 请求的 URL 是

https://maps.googleapis.com/maps/api/geocode/xml?address=3926+Linden+St,+Bethlehem,+PA+18020&key=MY_API_KEY

我的 SQL Server SP 代码是

CREATE PROCEDURE [dbo].[spGeocode]
 @Address varchar(80) = NULL OUTPUT,
 @City varchar(40) = NULL OUTPUT,
 @State varchar(40) = NULL OUTPUT,
 @Country varchar(40) = NULL OUTPUT,
 @PostalCode varchar(20) = NULL OUTPUT,

 @County varchar(40) = NULL OUTPUT,

 @GPSLatitude numeric(9,6) = NULL OUTPUT,
 @GPSLongitude numeric(9,6) = NULL OUTPUT,
 @MapURL varchar(1024) = NULL OUTPUT

 AS
 BEGIN
 SET NOCOUNT ON

 DECLARE @URL varchar(MAX)
 SET @URL = 'https://maps.googleapis.com/maps/api/geocode/xml?address=' +
 CASE WHEN @Address IS NOT NULL THEN @Address ELSE '' END +
 CASE WHEN @City IS NOT NULL THEN ', ' + @City ELSE '' END +
 CASE WHEN @State IS NOT NULL THEN ', ' + @State ELSE '' END +
 CASE WHEN @PostalCode IS NOT NULL THEN ', ' + @PostalCode ELSE '' END +
 CASE WHEN @Country IS NOT NULL THEN ', ' + @Country ELSE '' END +
 '&key=MY_API_KEY'
 SET @URL = REPLACE(@URL, ' ', '+')

 DECLARE @Response varchar(8000)
 DECLARE @XML xml
 DECLARE @Obj int 
 DECLARE @Result int 
 DECLARE @HTTPStatus int 
 DECLARE @ErrorMsg varchar(MAX)

 EXEC @Result = sp_OACreate 'MSXML2.ServerXMLHttp', @Obj OUT 



 BEGIN TRY
 EXEC @Result = sp_OAMethod @Obj, 'open', NULL, 'GET', @URL, false

 EXEC @Result = sp_OAMethod @Obj, 'setRequestHeader', NULL, 'Content-Type', 'application/x-www-form-urlencoded'

 EXEC @Result = sp_OAMethod @Obj, send, NULL, ''

 EXEC @Result = sp_OAGetProperty @Obj, 'status', @HTTPStatus OUT 

 EXEC @Result = sp_OAGetProperty @Obj, 'responseXML.xml', @Response OUT 

 END TRY
 BEGIN CATCH
 SET @ErrorMsg = ERROR_MESSAGE()
 END CATCH

 EXEC @Result = sp_OADestroy @Obj

 IF (@ErrorMsg IS NOT NULL) OR (@HTTPStatus <> 200) 
 BEGIN
 SET @ErrorMsg = 'Error in spGeocode: ' + ISNULL(@ErrorMsg, 'HTTP result is: ' + CAST(@HTTPStatus AS varchar(10)))
 RAISERROR(@ErrorMsg, 16, 1, @HTTPStatus)
 RETURN 
 END

 SET @XML = CAST(@Response AS XML)

 SET @GPSLatitude = @XML.value('(/GeocodeResponse/result/geometry/location/lat) [1]', 'numeric(9,6)')
 SET @GPSLongitude = @XML.value('(/GeocodeResponse/result/geometry/location/lng) [1]', 'numeric(9,6)')

 SET @City = ISNULL(
             ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="locality"]/long_name) [1]', 'varchar(40)'), 
                    @XML.value('(/GeocodeResponse/result/address_component[type="neighborhood"]/long_name) [1]', 'varchar(40)')
                    )
                    ,
                    @XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_3"]/long_name) [1]', 'varchar(40)')
                    )

 SET @State = @XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_1"]/short_name) [1]', 'varchar(40)') 
 SET @PostalCode = @XML.value('(/GeocodeResponse/result/address_component[type="postal_code"]/long_name) [1]', 'varchar(20)') 
 SET @Country = @XML.value('(/GeocodeResponse/result/address_component[type="country"]/short_name) [1]', 'varchar(40)') 
 SET @County = @XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_2"]/short_name) [1]', 'varchar(40)') 

 SET @Address = ISNULL((@XML.value('(/GeocodeResponse/result/address_component[type="street_number"]/long_name) [1]', 'varchar(40)') + 
                        ' ' +
                        @XML.value('(/GeocodeResponse/result/address_component[type="route"]/long_name) [1]', 'varchar(40)')
                        )
                        ,
                        @XML.value('(/GeocodeResponse/result/formatted_address) [1]', 'varchar(100)')
                        )

 SET @MapURL = 'https://maps.google.com/maps?f=q&hl=en&q=' + CAST(@GPSLatitude AS varchar(20)) + '+' + CAST(@GPSLongitude AS varchar(20))

 
 SELECT 
 @GPSLatitude AS GPSLatitude,
 @GPSLongitude AS GPSLongitude,
 @City AS City,
 @State AS [State],
 @PostalCode AS PostalCode,
 @Address AS [Address],
 @County AS County,
 @MapURL AS MapURL,
 @XML AS XMLResults


END

在这个特定的地址上,当我运行 SP 时

EXEC spGeocode '3926 Linden St, Bethlehem, PA 18020'

Geocode API 没有通过上述 SP 提供响应,但是当我在浏览器中输入整个 URL 时,它显示了有效的 XML 响应。还有许多其他有效地址遇到了同样的问题。 SQL Server SP 显示 HTTP 请求 STATUS 200,但根本没有错误消息,并且响应为 NULL,我认为这意味着没有给出响应。

更新 - 此问题仅发生在 Google Geocodes API 返回超过 1 个结果段的地址中。在这种情况下,responseXML.xml 变为 NULL。

我想知道如何解决这个问题。

【问题讨论】:

  • 返回什么错误响应(在 SQL Server 中)?
  • 没有返回错误。状态 200。它根本没有响应
  • 返回的有效负载中的任何内容(当您获得状态 200 时)?
  • 不幸的是 HTTPStatus 是唯一返回 200 的。没有返回任何其他信息。
  • 有没有responseText?

标签: sql-server xml google-maps http geocoding


【解决方案1】:

我终于明白了。

仅当特定地址在GeocodeResponses 下返回多个result 元素时,才会发生这种情况。发生这种情况时,responseXML.xml 的大小将写入@Response 变量 thru

EXEC @Result = sp_OAGetProperty @Obj, 'responseXML.xml', @Response OUT

超过了@Response VARCHAR(8000) 的预设长度,因此根本不会将任何内容写入@Response,这导致@HTTPStatus 为200,但没有@ErrorMsg@Response

解决方案是将响应的 XML 数据写入临时表或表变量,而不是将它们保存在 VARCHAR 中。修改后的代码应该(如果使用临时表)在提取 responseXML.xml 之前创建一个临时表,然后将响应写入该临时表。

ALTER PROCEDURE [dbo].[spAddressSearch]
 --Define input/output variables
 @Address varchar(80) = NULL OUTPUT,
 @City varchar(40) = NULL OUTPUT,
 @State varchar(40) = NULL OUTPUT,
 @Country varchar(40) = NULL OUTPUT,
 @PostalCode varchar(20) = NULL OUTPUT,
 @County varchar(40) = NULL OUTPUT,
 @GPSLatitude numeric(9,6) = NULL OUTPUT,
 @GPSLongitude numeric(9,6) = NULL OUTPUT,
 @MapURL varchar(3000) = NULL OUTPUT

 AS
 BEGIN
 SET NOCOUNT ON

 DECLARE @OrigAddress VARCHAR(80);
 SET @OrigAddress = @Address -- Create a separate address variable to future output

 --Define Google Geocoding API URL to look up for
 DECLARE @URL varchar(3000)
 SET @URL = 'https://maps.google.com/maps/api/geocode/xml?&address=' +
            CASE WHEN @Address IS NOT NULL THEN @Address ELSE '' END +
            CASE WHEN @City IS NOT NULL THEN ', ' + @City ELSE '' END +
            CASE WHEN @State IS NOT NULL THEN ', ' + @State ELSE '' END +
            CASE WHEN @PostalCode IS NOT NULL THEN ', ' + @PostalCode ELSE '' END +
            CASE WHEN @Country IS NOT NULL THEN ', ' + @Country ELSE '' END +
            '&key=MY_API_KEY' --This key must be generated on Google Cloud Platform first
 SET @URL = REPLACE(@URL, ' ', '+')


 --Prepare for HTTP request 
 DECLARE @XML xml
 DECLARE @Obj int 
 DECLARE @Result int 
 DECLARE @HTTPStatus int 
 DECLARE @ErrorMsg varchar(8000)

 EXEC @Result = sp_OACreate 'MSXML2.ServerXMLHttp', @Obj OUT 

 --Create a temp table to hold XML values returned. 
 --Due to the size of XML values exceed VARCHAR(8000) for a number of stores returned, it must be stored in a table (or table variable).
 IF OBJECT_ID('tempdb..#xml') IS NOT NULL DROP TABLE #xml
 CREATE TABLE #xml ( XMLValue XML )

 --Send HTTP request
 BEGIN TRY
     EXEC @Result = sp_OAMethod @Obj, 'open', NULL, 'GET', @URL, 'false'
     EXEC @Result = sp_OAMethod @Obj, 'setRequestHeader', NULL, 'Content-Type', 'application/x-www-form-urlencoded'
     EXEC @Result = sp_OAMethod @Obj, send, NULL, ''
     EXEC @Result = sp_OAGetProperty @Obj, 'status', @HTTPStatus OUT 
     -- Cast value to the temp table created earlier
     INSERT  #xml (XMLValue)
     EXEC @Result = sp_OAGetProperty @Obj, 'responseXML.xml'--
 
  
 END TRY
 BEGIN CATCH --See if there is bad request
    SET @ErrorMsg = ERROR_MESSAGE()
 END CATCH
 EXEC @Result = sp_OADestroy @Obj

 --Error handling: output bad data to the same table
 IF (@ErrorMsg IS NOT NULL) OR (@HTTPStatus <> 200) 
 BEGIN

     SET @GPSLatitude = 999.999999
     SET @GPSLongitude = 999.999999
     SET @City = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @State = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @PostalCode = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @Country = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @County = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @Address = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @MapURL = 'Error: ' + ISNULL(@ErrorMsg, 'HTTP: ' + CAST(@HTTPStatus AS varchar(10)))
     SET @XML = (SELECT XMLValue FROM #XML)

     SELECT 
     @OrigAddress AS OriginalAddress,
     @GPSLatitude AS GPSLatitude,
     @GPSLongitude AS GPSLongitude,
     @City AS City,
     @State AS [State],
     @PostalCode AS PostalCode,
     @Address AS [Address],
     @County AS County,
     @MapURL AS MapURL,
     @XML AS XMLResults

     SET @ErrorMsg = 'Error in spGeocode: ' + ISNULL(@ErrorMsg, 'HTTP result is: ' + CAST(@HTTPStatus AS varchar(10)))

 END

 --If no error raised and status = 200- meaning the request is captured OK
 IF (@ErrorMsg IS NULL) AND (@HTTPStatus = 200) 
 BEGIN
     DECLARE @TempAddress VARCHAR(500)
     
     SET @XML = (SELECT XMLValue FROM #XML)
     SET @GPSLatitude = ISNULL(@XML.value('(/GeocodeResponse/result/geometry/location/lat) [1]', 'numeric(9,6)'),999.999999)
     SET @GPSLongitude = ISNULL(@XML.value('(/GeocodeResponse/result/geometry/location/lng) [1]', 'numeric(9,6)'),999.999999)
     SET @City = ISNULL(
                 ISNULL(
                 ISNULL(
                 ISNULL(
                 ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="locality"]/long_name) [1]', 'varchar(40)'), 
                        @XML.value('(/GeocodeResponse/result/address_component[type="sublocality"]/long_name) [1]', 'varchar(40)')),
                        @XML.value('(/GeocodeResponse/result/address_component[type="neighborhood"]/long_name) [1]', 'varchar(40)')),
                        @XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_3"]/short_name) [1]', 'varchar(40)')),
                        @XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_1"]/short_name) [1]', 'varchar(40)')),
                        'Not Found in Google Maps')
     SET @State = ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_1"]/short_name) [1]', 'varchar(40)'), 'Not Found in Google Maps') 
     SET @PostalCode = ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="postal_code"]/long_name) [1]', 'varchar(20)'), 'Not Found in Google Maps') 
     SET @Country = ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="country"]/short_name) [1]', 'varchar(40)'), 'Not Found in Google Maps') 
     SET @County = ISNULL(@XML.value('(/GeocodeResponse/result/address_component[type="administrative_area_level_2"]/short_name) [1]', 'varchar(40)'),'Not Found in Google Maps')
     
     SET @Address = ISNULL(ISNULL(
                           @XML.value('(/GeocodeResponse/result/formatted_address) [1]', 'varchar(200)'),
                           (@XML.value('(/GeocodeResponse/result/address_component[type="street_number"]/long_name) [1]', 'varchar(40)') + ' ' +
                            @XML.value('(/GeocodeResponse/result/address_component[type="route"]/long_name) [1]', 'varchar(40)')))
                            , 'Not Found in Google Maps')
                              
     SET @MapURL = 'https://maps.google.com/maps?f=q&hl=en&q=' + CAST(@GPSLatitude AS varchar(20)) + '+' + CAST(@GPSLongitude AS varchar(20))

 
     SELECT 
     @OrigAddress AS InputAddress,
     --@HTTPStatus,@ErrorMsg,
     @GPSLatitude AS GPSLatitude,
     @GPSLongitude AS GPSLongitude,
     @City AS City,
     @State AS [State],
     @PostalCode AS PostalCode,
     @Address AS [Address],
     @County AS County,
     @MapURL AS MapURL,
     @XML AS XMLResults
     
     END

END


GO

【讨论】:

  • 我也在为同样的问题而苦苦挣扎,不知道发生了什么。你的想法很完美,它就像一个魅力!干得好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 2013-10-31
  • 2017-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多