目录
2、第二关UnsupportedMimeTypeException
前言
如果你现在的项目中有如下的需求,比如已知一个中国境内的地名地址信息,然后需要知道这个地名地址对应的位置信息,比如经纬度信息。要实现上面的需求呢,比如可以通过其它的地图厂商,比如百度地图或者高德地图的开放API接口来实现将文字的地理位置转换为经纬度的位置信息。这是直接调用在线服务的方式,当然我们也可以自己整理基础数据,比如去互联网上搜集别人分享的地名地址信息还有村镇行政区划信息,当有了这些基础的地理数据之后,也可以自己构建私有的地名服务,但是这样需要的信息量是比较大的,也是比较困难的,同时数据的准确性和及时程度都是有一定的延迟的。对基础数据的搜集、清理、存储、检索做成服务化的应用功能。当前在前面的在线服务调用中,可能存在一个服务收费的问题,比如超过了一定的次数就会收费。
本文分享一种有一定使用次数限制的地理地名地址转换成经纬度的在线服务接口。那就是国家开放的天地图web接口,通过注册成为天地图网站的正式用户,就可以使用它的在线API,虽然面向普通开发者,它也是有一定的次数限制,但是如果我们的量不大,甚至说是一些固定的场景,比如有限的地名地址查找,一天的调用次数是少于3000的,其实是可以满足我们的日常需求的。而且天地图的信息是国家地理中心发布,具有较高的权威性。在之前的一篇Leaflet进行天地图的服务调用的博客中,原文地址:基于天地图使用Leaflet.js进行WebGIS开发实战。博客中讲解的是前端的调用方式,如果我们想将一些地址转换成地理坐标后,还可以保存起来,就需要使用后端服务调用的方式。
本文即以Java语言为例,我们使用Jsoup作为接口请求工具来访问天地图的地理编码接口。详细讲解在调用天地图的地理编码接口中会遇到的一些艰难险阻,通过解决接口调用过程中的这些问题,掌握如何正确的进行jsoup调用天地图的服务。
一、天地图的地理编码接口
最开始的时候还是将天地图的地理编码接口给大家介绍一下,让大家有个基本的了解。在天地图的官网上找到地理编码接口,如下图所示:
1、相关的API介绍
关于天地图的API,我们直接来看官方网站上的介绍,关于请求参数和响应的参数,官网上都定义得比较详细。天地图地理编码API是一类简单的HTTP/HTTPS接口,是指由结构化地址数据(如:北京市海淀区莲花池西路28号)转换为对应坐标点(经纬度)功能,地址解析仅限于国内。 使用地理编码服务前您需要申请Key。
参数值 | 参数说明 | 参数类型 | 是否必备 | 备注(值域) |
keyWord | 请求关键字 | string | 是 | 无 |
请求: http://api.tianditu.gov.cn/geocoder?ds={"keyWord":"北京市延庆区延庆镇莲花池村前街50夕阳红养老院"}&tk=您的密钥
{
"location": {
"lon": "116.001688",
"level": "地名地址",
"lat": "40.453228"
},
"status": "0",
"msg": "ok",
"searchVersion": "4.8.0"
}
2、响应接口
在调用了相应的服务接口后,可以看到如上的相应数据。来看一下官网的响应接口参数。
参数值 | 参数说明 | 参数类型 | 备注(值域) |
status | 返回状态 | string | 0:正常返回,101:结果为空,404:出错。 |
msg | 返回信息 | string | OK:正常,其他异常。 |
location | 地址信息 | json | 地址信息 |
其中locaction地址信息的对象如下:
参数值 | 参数说明 | 参数类型 | 备注(值域) |
lon | 坐标点显示经度 | Double | 必须返回。 |
lat | 坐标点显示纬度 | Double | 必须返回。 |
level | 类别名称 | string | 非必须返回。 |
typeRound | 附近相似点 | Array | 开启周边查询必需返回。 |
上面对将要使用的接口进行介绍,方便大家后面在调用时有所了解。下面将结合java语言来重点讲解如何进行接口的调用。
二、使用JSOUP调用相应接口面对的问题及应对
在了解了天地图的相关接口后,我们即以java开发语言为例,使用Jsoup来进行天地图的接口调用。详细记录在使用Jsoup进行接口访问时遇到的常见问题,以及如何解决上面的问题,正确的获取我们的响应信息,并转换成我们想要的java对象。
1、第一关访问路径的问题
首先我们在后台按照官方网站的要求,将代码改写成直接请求的方式,具体的代码如下所示:
/**
* 普通请求测试,请求字符串未转义,会报错java.net.MalformedURLException
*/
@Test
public void testSimpleTdtQuery() {
String address = "长郡梅溪湖中学";
String queryParams = "ds={'keyWord':'" + address + "'}";
String url = TDT_ADDRESS_API + queryParams+"'&tk=" + TDT_SERVER_KEY;
System.out.println(url);
Document document;
try {
document = Jsoup.connect(url).get();
Elements elements = document.select("body");
System.out.println(elements.get(0).html().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
上述代码表示连接天地图的官方接口,接着我们来运行一下,会发现程序报如下的错误:
错误代码如下,大致的错误:
https://api.tianditu.gov.cn/geocoder?ds={'keyWord':'长郡梅溪湖中学'}'&tk=473af7dc18cafb6b993616a0ce8e1ead
java.net.MalformedURLException: Illegal character in query at index 40: https://api.tianditu.gov.cn/geocoder?ds={'keyWord':'长郡梅溪湖中学'}'&tk=473af7dc18cafb6b993616a0ce8e1ead
at org.jsoup.helper.CookieUtil.asUri(CookieUtil.java:80)
at org.jsoup.helper.CookieUtil.applyCookiesToRequest(CookieUtil.java:41)
at org.jsoup.helper.HttpConnection$Response.createConnection(HttpConnection.java:1033)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:856)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:829)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:366)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:353)
可以看到,这里提示的错误信息是访问的URL有问题,在访问地址的40个字符的位置,就是{}这里,通过提示就大概指导了如何解决。我们将这个花括号进行转义即可。解决的办法很简单,只要将访问的参数地址换成以下的地址即可。
String queryParams = "ds=%7B'keyWord':'" + address + "'%7D";
修改后再执行就不会再报这个url路径的问题了。
2、第二关UnsupportedMimeTypeException
在顺利通过第一关的接口访问后,本来是期望可以正常访问到数据的,但是一波未平,一波又起,解决了访问路径的问题,又遇到以下的问题。我们提供访问路径修正后的代码:
/**
* 普通请求测试,未设置忽略content_type,会报错org.jsoup.UnsupportedMimeTypeException
*/
@Test
public void testSimpleTdtQueryWithIgnoreContentType() {
String address = "长郡梅溪湖中学";
String queryParams = "ds=%7B'keyWord':'" + address + "'%7D";
String url = TDT_ADDRESS_API + queryParams+"'&tk=" + TDT_SERVER_KEY;
System.out.println(url);
Document document;
try {
document = Jsoup.connect(url).get();
Elements elements = document.select("body");
System.out.println(elements.get(0).html().toString());
Gson gson = new Gson();
TdtAddressInfo info = gson.fromJson(elements.get(0).html().toString(), TdtAddressInfo.class);
System.out.println(info);
System.out.println(info.getLocation().getLon());
} catch (IOException e) {
e.printStackTrace();
}
}
运行后的结果如下所示:
显示的关键错误信息如下:
https://api.tianditu.gov.cn/geocoder?ds=%7B'keyWord':'长郡梅溪湖中学'%7D'&tk=473af7dc18cafb6b993616a0ce8e1ead
org.jsoup.UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/*+xml. Mimetype=application/json; charset=UTF-8, URL=https://api.tianditu.gov.cn/geocoder?ds=%7B'keyWord':'%E9%95%BF%E9%83%A1%E6%A2%85%E6%BA%AA%E6%B9%96%E4%B8%AD%E5%AD%A6'%7D'&tk=473af7dc18cafb6b993616a0ce8e1ead
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:900)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:829)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:366)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:353)
at com.yelang.project.education.TestViolationTrainingCase.testSimpleTdtQueryWithUserAgent(TestViolationTrainingCase.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
从上面的提示信息来看,关键的主体信息是org.jsoup.UnsupportedMimeTypeException:Unhandled content type. Must be text/*, application/xml, or application/*+xml. Mimetype=application/json; charset=UTF-8,可以看到是响应的内容类型不受支持。那么这个问题怎么解决呢?
其实解决办法很简单,在jsoup中,我们可以设置忽略,设置方法如下:
document = Jsoup.connect(url)
.ignoreContentType(true)
.get();
上面的设置就是忽略contenttype,不做任何转换。直接返回字符信息。将响应结果从后台获取后,我们就可以进行数据转换。将json数据转换为java对象。我们来定义对应的java实体类:
package com.yelang.project.education.domain;
import java.io.Serializable;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class TdtAddressInfo implements Serializable{
private static final long serialVersionUID = -1013000744231947989L;
private String msg;//OK:正常,其他异常。
private String status;//0:正常返回,101:结果为空,404:出错。
private String searchVersion;//查询版本
private TdtLocation location;//位置信息
}
package com.yelang.project.education.domain;
import java.io.Serializable;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class TdtLocation implements Serializable{
private static final long serialVersionUID = 5155681441498287691L;
private Double lon;//lon 坐标点显示经度 Double 必须返回。
private Double lat;//lat 坐标点显示纬度 Double 必须返回。
private String level;//类别名称 string 非必须返回。
private String typeRound;//附近相似点 Array 开启周边查询必需返回。
private Double score;//打分
}
经过转换后的程序运行结果如下:
https://api.tianditu.gov.cn/geocoder?ds=%7B'keyWord':'长郡梅溪湖中学'%7D'&tk=473af7dc18cafb6b993616a0ce8e1ead
{"msg":"ok","location":{"score":81,"level":"兴趣点","lon":"112.870830","lat":"28.192800","keyWord":"长郡梅溪湖中学"},"searchVersion":"6.4.9V","status":"0"}
TdtAddressInfo(msg=ok, status=0, searchVersion=6.4.9V, location=TdtLocation(lon=112.87083, lat=28.1928, level=兴趣点, typeRound=null, score=81.0))
112.87083
经过上述的编码设置即完成地理编码接口的调用,也返回了相应的地理信息。
3、可能的http获取403问题
在访问的过程中,如果顺利的话,可能你不会遇到请求403报错的问题。类似于下面的这种错误。
org.jsoup.HttpStatusException: HTTP error fetching URL. Status=403, URL=http://www.example.com
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:537)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:493)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:205)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:194)
at com.javacodeexamples.libraries.jsoup.Jsoup403ForbiddenExample.main(Jsoup403ForbiddenExample.java:19)
如果在实际使用过程当中遇到这种问题,您可以采用下面的方法进行应对。即可设置用户的user-agent来进行解决。
document = Jsoup.connect(url)
.header("User-Agent", DEFAULT_USER_AGENT)//添加user-agent 可以避免403的报错
.ignoreContentType(true)//设置忽略可以防止org.jsoup.UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/xhtml+xml
.get();
private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2";
如果遇到上面的问题,按照上面的解决办法来修复即可。
三、总结
以上就是本文的主要内容, 本文即以Java语言为例,我们使用Jsoup作为接口请求工具来访问天地图的地理编码接口。详细讲解在调用天地图的地理编码接口中会遇到的一些艰难险阻,通过解决接口调用过程中的这些问题,掌握如何正确的进行jsoup调用天地图的服务。行文仓促,难免有很多不足之处,针对不足还肯定各位专家博主批评指正,不胜感激。