Android- 环信IM即时通讯_RESTAPI(2)

*****************************************************REST_API 简介*******************************************************
1.理解OAuth 2.0 :http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
2.理解RESTful架构 :http://www.ruanyifeng.com/blog/2011/09/restful.html
3.RESTful API 设计指南 : http://www.ruanyifeng.com/blog/2014/05/restful_api.html
4.服务器返回码参数表: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html



A)REST client:HTTPCLIENT>JSON
REST client就是调用REST API的程序端,这里我们用的是Android客户端;调用REST API, 本质就是发送HTTP请求, 只不过大家常用的可能是 HTTP GET 和 HTTP POST_请求, 
但是在REST 里面还经常用到 _HTTP PUT 和 HTTP DELETE, 在REST中, 把这四种操作称之为动词, 可以(不是特别准确)的想象成增删改查.
而动词所操作的对象, 在REST中, 被称之为_资源, 也就是_URL;


环信的REST API都是基于json的, 所以在构造_HTTP_ 请求的时候, 需要在_HTTP HEADER_中指明:

header_name         header_value description
Accept                 application/json 服务器端返回给客户端的数据类型
Content-Type application/json 客户端发到服务器端的是数据类型



在Java中, REST Client实现方式有多种,比如JBOss RestEasy、 Sun Jersey、Dropwizard、Apache HTTPClient.我们这里直接使用Apache Http Client.


一个DEMO
假设一个企业id为 easemob-demo, 然后这个企业下面有个app名字叫做 chatdemoui, 那么环信的REST API就是下面的样子:
Path : URI
HTTP Method : GET/POST/PUT/DELETE
Request Headers : {
			Content-Type : application/json,
			Accept: application/json,
    	Authorization : Bearer ${token}
}
Request Body : JSON


名词 解 释
org_name 企业的唯一标识,开发者在环信开发者管理后台注册账号时填写的企业ID
app_name 同一”企业”下”app”唯一标识,开发者在环信开发者管理后台创建应用时填写的”应用名称”
org_admin 开发者在环信开发者管理后台注册时填写的”用户名”.企业管理员拥有对该企业账号下所有资源的操作权限
app_admin 应用管理员,具有admin权限的一个特殊IM用户,拥有对该应用下所有资源的操作权限
appkey   一个app的唯一标识,规则是 ${org_name}#${app_name}





*******************************************************用户体系集成*********************************************************
1.环信ID规则
>本文档中可能会交错使用”环信ID”和”环信用户名”两个术语, 但是请注意, 这里两个的意思是一样的
>推荐使用 md5 函数, 对用户名进行加密


2.获取APP管理员Token
使用app的client_id和client_secret获取授权管理员token,可以在环信管理后台的app详情页面看到
发送
Path : /{org_name}/{app_name}/token
HTTP Method : POST
URL Params : 无
Request Headers : {“Content-Type”:”application/json”}
Request Body : {“grant_type”: “client_credentials”,”client_id”: “{app的client_id}”,”client_secret”: “{app的client_secret}”}



key value
access_token token值
expires_in 有效时间,秒为单位, 默认是七天,在有效期内是不需要重复获取的
application 当前app的UUID值



可能的错误码: 
400 (client_id或client_secret错误) 
5xx 
详见:[REST接口错误码] (http://www.easemob.com/docs/helps/errorcodes/)


3.注册IM用户[单个]
在url指定的org和app中创建一个新的用户,分两种模式:开放注册 和 授权注册


>开放注册
Path : /{org_name}/{app_name}/users
HTTP Method : POST
URL Params : 无
Request Headers : {“Content-Type”:”application/json”}
Request Body : {“username”:”${用户名}”,”password”:”${密码}”, “nickname”:”${昵称值}”}


>授权注册
Path : /{org_name}/{app_name}/users
HTTP Method : POST
URL Params : 无
Request Headers : 
		{
		“Content-Type”:”application/json”,
		 ”Authorization”:”Bearer ${token}”
		}
Request Body : {“username”:”${用户名}”,”password”:”${密码}”}


4.注册IM用户[批量]
Path : /{org_name}/{app_name}/users
HTTP Method : POST
URL Params : 无
Request Headers : {“Content-Type”:”application/json”,”Authorization”:”Bearer ${token}”}
Request Body : [{“username”:”${用户名1}”,”password”:”${密码}”},…,{“username”:”${用户名2}”,”password”:”${密码}”}]

Response 示例:
{
  "action" : "post",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "path" : "/users",
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users",
  "entities" : [ entitie,entitie,entitie,entitie ],
  "timestamp" : 1409570811312,
  "duration" : 802,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}
{
    "uuid" : "de86365a-31ca-11e4-aecf-9509b836c0d6",
    "type" : "user",
    "created" : 1409570811445,
    "modified" : 1409570811445,
    "username" : "u2",
    "activated" : true
}entitie


获取IM用户[单个]
Path : /{org_name}/{app_name}/users/{username}
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}


5.获取IM用户[批量]
该接口默认返回最近创建的10个用户,如果需要指定获取数量,需加上参数limit=N,N为数量值. 关于分页:如果DB中的数量大于N,
返回json会携带一个字段“cursor”,我们把它叫做”游标”,该游标可理解为结果集的指针,值是变化的。往下取数据的时候带着游标,
就可以获取到下一页的值。如果还有下一页,返回值里依然还有这个字段,直到没有这个字段,说明已经到最后一页。
cursor的意义在于数据(真)分页。
Path : /{org_name}/{app_name}/users
HTTP Method : GET
//指定条数
URL Params : limit=20
//cursor:用户开始索引
URL Params : limit=20&cursor=LTU2ODc0MzQzOnNmdTlxdF9LRWVPaVFvMWlBZmc4S3c

Request Headers : {“Authorization”:”Bearer ${token}”}


Response 示例:
{
    "action" : "get",
    "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
    "params" : {
    "limit" : [ "20" ]
    },
    "path" : "/users",
    "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users?ql=select+*+from+null&limit=20",
    "entities" : [ entitie,entitie,entitie,entitie ]
}

{
    "uuid" : "fff15c10-df37-11e3-843f-e5b88d483c56",
    "type" : "user",
    "created" : 1400491736144,
    "modified" : 1409055655016,
    "username" : "wjglpgecxu",
    "activated" : true,
    "nickname" : "wjglpgecxu",
    "notifier_name" : "chatdemoui_dev"
}entitie


6.删除IM用户[单个]
Path : /{org_name}/{app_name}/users/{username}
HTTP Method : DELETE
URL 参数 : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :无


Response 示例:
{
  "action" : "delete",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "path" : "/users",
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users",
  "entities" : [ entitie,entitie,entitie,entitie ],
  "timestamp" : 1409576121910,
  "duration" : 3330,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}

{
    "uuid" : "628a88ba-dfce-11e3-8cac-51d3cb69b303",
    "type" : "user",
    "created" : 1400556326075,
    "modified" : 1400556326075,
    "username" : "ywuxvxuir6",
    "activated" : true
}entitie


7.删除IM用户[批量]
按时间段来删除: 使用ql=created> {起始时间戳} and created < {结束时间戳} 的查询语句, 时间戳是timestamp类型的, 并且需要对ql进行http url encode
DELETE /{org_name}/{app_name}/users?ql=created > 1409506121910 and created < 1409576121910
Path : /{org_name}/{app_name}/users
HTTP Method : DELETE
URL Params : limit=30
Request Headers : {“Authorization”:”Bearer ${token}”}


Response 示例:
{
  "action" : "delete",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : {
    "limit" : [ "5" ]
  },
  "path" : "/users",
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users",
  "entities" : []
}


8.重置IM用户密码
Path : /{org_name}/{app_name}/users/{username}/password
HTTP Method : PUT
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body : {“newpassword” : “${新密码指定的字符串}”}


Response 示例:
{
  "action" : "set user password",
  "timestamp" : 1409575962124,
  "duration" : 326
}


9.修改用户昵称
Path : /{org_name}/{app_name}/users/{username}
HTTP Method : PUT
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body : {“nickname” : “${昵称值}”}

Response 示例:
{
  "action" : "put",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "path" : "/users",
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users",
  "entities" : [ {
    "uuid" : "c3b56d5a-7135-11e4-92d2-edab82ae2302",
    "type" : "user",
    "created" : 1416543645861,
    "modified" : 1416550240537,
    "username" : "jianguo",
    "activated" : true,
    "device_token" : "61491f49f3e69cd1d62c5b390e42f4b1cd15bf1a876a487268cfaef9960188ee",
    "nickname" : "张建国"
  } ],
  "timestamp" : 1416550240285,
  "duration" : 278,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}


*******************************************************好友管理*********************************************************
1.给IM用户的添加好友
Path : /{org_name}/{app_name}/users/{owner_username}/contacts/users/{friend_username}
HTTP Method : POST
Request Headers : {“Authorization”:”Bearer ${token}”}


{
    "action":"post","application":"4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5","params":{},
    "path":"/users/aa6160da-eb01-11e3-ab09-15edd986e7b7/contacts",
    "uri":"http://a1.easemob.com/easemob-demo/chatdemoui/users/jliu/contacts/yantao",
    "entities":[ entitie ],
    "timestamp":1406086326974,"duration":242,
    "organization":"easemob-demo",
    "applicationName":"chatdemoui"
}


2.查看好友
Path : /{org_name}/{app_name}/users/{owner_username}/contacts/users
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}


{
  "action" : "get",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui/users/v3y0kf9arx/contacts/users",
  "entities" : [ ],
  "data" : [ "88888" ],
  "timestamp" : 1409737366071,
  "duration" : 45,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}


3.解除IM用户的好友关系
Path : /{org_name}/{app_name}/users/{owner_username}/contacts/users/{friend_username}
HTTP Method : DELETE
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body : 无


{
  "action" : "delete",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "path" : "/users/stliu/contacts",
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemo/users/stliu/contacts/users/yantao",
  "entities" : [ entitie ],
  "timestamp" : 1409739808288,
  "duration" : 1575,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}


***************************************************群组管理*********************************************************
1.获取app中所有的群组ID
<pre name="code" class="java">Path : /{org_name}/{app_name}/chatgroups
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}




2.获取一个用户参与的所有群组
Path : /{org_name}/{app_name}/users/{username}/joined_chatgroups
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}


Response 示例:
{
  ...
  "entities" : [ ],
  "data" : [ {
    "groupid" : "1413193977786197",
    "groupname" : "kenshingrou"
  }, {
    "groupid" : "1413194058403881",
    "groupname" : "kenshinngr1"
  } ],
  "timestamp" : 1413428676499,
  "duration" : 80
}


3.获取一个或者多个群组的详情
Path : /{org_name}/{app_name}/chatgroups/{group_id1},{group_id2}
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}


Response 示例:
{
  ...
  "entities" : [ ],
  "data" : [ {
    "id" : "1408518613503",
    "name" : "Jay13800138000",
    "description" : "",
    "public" : false,
    "membersonly" : true,
    "allowinvites" : false,
    "maxusers" : 200,
    "affiliations_count" : 3,
    "affiliations" : [ {
      "owner" : "13800138001"
     }, {
      "member" : "v3y0kf9arx"
     }, {
      "member" : "xc6xrnbzci"
     } ]
  } ],
  ...
}


4.获取群组中的所有成员
Path : /{org_name}/{app_name}/chatgroups/{group_id}/users
HTTP Method : GET
Request Headers : {“Authorization”:”Bearer ${token}”}


Response 示例:
{
  ...
  "entities" : [ ],
  "data" : [ {
    "member" : "lidis"
  }, {
    "member" : "asdfgh"
  }, {
    "owner" : "ruson"
  } ],
  "timestamp" : 1413012431449,
  "duration" : 24
}


5.创建/删除群
>>>创建
Path : /{org_name}/{app_name}/chatgroups
HTTP Method : POST
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :
{
    "groupname":"testrestgrp12", //群组名称, 此属性为必须的
    "desc":"server create group", //群组描述, 此属性为必须的
    "public":true, //是否是公开群, 此属性为必须的
    "maxusers":300, //群组成员最大数(包括群主), 值为数值类型,默认值200,此属性为可选的
    "approval":true, //加入公开群是否需要批准, 没有这个属性的话默认是true, 此属性为可选的
    "owner":"jma1", //群组的管理员, 此属性为必须的
    "members":["jma2","jma3"] //群组成员,此属性为可选的,但是如果加了此项,数组元素至少一个
}
Response 示例:
{
  "action" : "post",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui",
  "entities" : [ ],
  "data" : {
    "groupid" : "1411527886490154"
  },
  "timestamp" : 1411527886457,
  "duration" : 125,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}


>>>删除
Path : /{org_name}/{app_name}/chatgroups/{group_id}
HTTP Method : DELETE
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :无

Response 示例:
{
  "action" : "delete",
  "application" : "4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5",
  "params" : { },
  "uri" : "https://a1.easemob.com/easemob-demo/chatdemoui",
  "entities" : [ ],
  "data" : {
    "success" : true,
    "groupid" : "1411527886490154"
  },
  "timestamp" : 1411528112078,
  "duration" : 15,
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}

6.群组加/减人
>>添加单人
Path : /{org_name}/{app_name}/chatgroups/{group_id}/users/{user_primary_key}
HTTP Method : POST
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :无

Response 示例:
{
  ...
  "entities" : [ ],
  "data" : {
    "action" : "add_member",
    "result" : true,
    "groupid" : "1411816013089",
    "user" : "q4xpsfjfvf"
  },
  ...
}


>>群组批量添加成员
Path : /{org_name}/{app_name}/chatgroups/{chatgroupid}/users
HTTP Method : POST
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :{“usernames”:[“username1”,”username2”]}’ — usernames固定属性,
				作为json的KEY;username1/username2 要添加到群中的成员用户名,可变

>>删除单人
Path : /{org_name}/{app_name}/chatgroups/{group_id}/users/{user_primary_key}
HTTP Method : DELETE
URL Params : 无
Request Headers : {“Authorization”:”Bearer ${token}”}
Request Body :无
Response 示例:
{
  ...
  "entities" : [ ],
  "data" : {
    "action" : "remove_member",
    "result" : true,
    "groupid" : "1411816013089",
    "user" : "q4xpsfjfvf"
  },
  ...
  "organization" : "easemob-demo",
  "applicationName" : "chatdemoui"
}


***************************************************消息管理*********************************************************
>>查看用户状态
查看一个用户的在线状态
Path : /{org_name}/{app_name}/users/{username}/status
HTTP Method : GET
URL Params : 无
Request Headers : {“Content-Type”:”application/json”,”Authorization”:”Bearer ${token}”}
Request Body : 无
{
    ...
    "data": {
        "stliu": "online"  //注意, 这里返回的是用户名和在线状态的键值对, 值为 online 或者 offline
    },
    ...
}




>>发送消息
Path : /{org_name}/{app_name}/messages
Request Method : POST
Request Headers : {“Content-Type”:”application/json”,”Authorization”:”Bearer ${token}”}
Request Body :
{
    "target_type" : "users", // users 给用户发消息, chatgroups 给群发消息
    "target" : ["u1", "u2", "u3"], // 注意这里需要用数组,数组长度建议不大于20, 即使只有一个用户,   
                                   // 也要用数组 ['u1'], 给用户发送时数组元素是用户名,给群组发送时  
                                   // 数组元素是groupid
    >>发送文本
    "msg" : {
        "type" : "txt",
        "msg" : "hello from rest" //消息内容,(http://developer.easemob.com/docs/emchat/rest/chatmessage.html)里的bodies内容
        },
        
    >>发送图片
    "msg" : {  
        "type" : "img",   // 消息类型
	    	"url": "https://a1.easemob.com/easemob-demo/chatdemoui/chatfiles/55f12940-64af-11e4-8a5b-ff2336f03252",  //成功上传文件返回的uuid
	    	"filename": "24849.jpg", // 指定一个文件名
	    	"secret": "VfEpSmSvEeS7yU8dwa9rAQc-DIL2HhmpujTNfSTsrDt6eNb_" // 成功上传文件后返回的secret
     },
    
    >>发送语音
    "msg" : {   
				"type": "audio",  // 消息类型
				"url": "https://a1.easemob.com/easemob-demo/chatdemoui/chatfiles/1dfc7f50-55c6-11e4-8a07-7d75b8fb3d42",  //成功上传文件返回的uuid
				"filename": "messages.amr", // 指定一个文件名
				"length": 10,
				"secret": "Hfx_WlXGEeSdDW-SuX2EaZcXDC7ZEig3OgKZye9IzKOwoCjM" // 成功上传文件后返回的secret
		},
		
		>>发送视频
		"msg": { //消息内容
        "type": "video",// 消息类型
        "filename": "1418105136313.mp4",// 视频文件名称
        "thumb": "http://a1.easemob.com/easemob-demo/chatdemoui/chatfiles/67279b20-7f69-11e4-8eee-21d3334b3a97",//成功上传视频缩略图返回的uuid
        "length": 10,//视频播放长度
        "secret": "VfEpSmSvEeS7yU8dwa9rAQc-DIL2HhmpujTNfSTsrDt6eNb_",// 成功上传视频文件后返回的secret
        "file_length": 58103,//视频文件大小
        "thumb_secret": "ZyebKn9pEeSSfY03ROk7ND24zUf74s7HpPN1oMV-1JxN2O2I",// 成功上传视频缩略图后返回的secret
        "url": "http://a1.easemob.com/easemob-demo/chatdemoui/chatfiles/671dfe30-7f69-11e4-ba67-8fef0d502f46"//成功上传视频文件返回的uuid
    },
    
    >>发送透传
    "msg":{  //消息内容
				"type":"cmd",  // 消息类型
				"action":"action1"
		},
        
    "from" : "jma2", //表示这个消息是谁发出来的, 可以没有这个属性, 那么就会显示是admin, 如果有的话, 则会显示是这个用户发出的    
    "ext" : { //扩展属性, 由app自己定义.可以没有这个字段,但是如果有,值不能是“ext:null“这种形式,否则出错
        "attr1" : "v1",
        "attr2" : "v2"
    }    
}



Response 示例:
{
    ...
    "data": {
        "stliu1": "success",
        "jma3": "success",
        "stliu": "success",
        "jma4": "success"
    },
    ...
}


****************************************************T I P******************************************************


REST接口调用成功时返回HTTP状态码为200,返回数据结果为标准Json格式。
如调用错误会返回除200之外的其他HTTP状态码,返回数据结果也为标准Json格式,可根据返回数据中的error字段判断具体错误。


http状态返回代码 4xx(请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。 
http状态返回代码 5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。


HTTP 返回码(statuscode) 说明(description)
400 (错误请求) 服务器不理解请求的语法。
401 (未授权) 请求要求身份验证。 对于需要token的接口,服务器可能返回此响应。
403 (禁止) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的接口。
408 (请求超时) 服务器等候请求时发生超时。
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。


HTTP statuscode error error_description
400 invalid_grant invalid username or password
用户名或者密码输入错误
400 organization_application_not_found “Could not find application for easemob-demo/aachatdemoui from URI: easemob-demo/aachatdemoui/users”
找不到aachatdemoui对应的app, 可能是URL写错了
400 illegal_argument “Entity user requires a property named username”
创建用户请求体未提供”username”
400 illegal_argument “password or pin must provided”
创建用户请求体未提供”password”
400 json_parse “Unexpected character (‘=’ (code 61)): was expecting a colon to separate field name and value\n at [Source: java.io.BufferedInputStream@170e3f35; line: 1, column: 23]”
发送请求时请求体不符合标准的JSON格式,服务器无法正确解析
400 illegal_argument “password or pin must provided”
注册用户时json中提供了password但是值未空字符
400 duplicate_unique_property_exists “Application 4d7e4ba0-dc4a-11e3-90d5-e1ffbaacdaf5Entity user requires that property named username be unique, value of dddd exists”
用户名已存在, dddd这个用户名在该app下已经存在
400 illegal_argument “newpassword is required”
修改用户密码的请求体没提供newpassword属性
400 illegal_argument “group member username1 doesn’t exist”
批量添加群组时预加入群组的新成员username不存在
401 unauthorized “registration is not open, please contact the app admin”
app的用户注册模式为授权注册,但是注册用户时请求头没带token
401 auth_bad_access_token “Unable to authenticate due to corrupt access token”
发送请求时使用的token错误, 注意:不是token过期
401 auth_bad_access_token “Unable to authenticate”
无效token, 符合token的格式,但是该token不是接受请求的系统生成的,系统无法识别该token
401 “Unable to authenticate due to expired access token”
token过期
404 service_resource_not_found “Service resource not found”
URL指定的资源不存在
500 no_full_text_index “Entity ‘user’ with property named ‘username’ is not full text indexed. You cannot use the ‘contains’ operand on this field”
username不支持全文索引,不可以对该字段进行contains操作
500 unsupported_service_operation “Service operation not supported”
请求方式不被发送请求的URL支持
500 web_application “javax.ws.rs.WebApplicationException” 错误的请求, 
给一个未提供的API发送了

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。