Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wd1105040417
retrofit
提交
a1cee2eb
R
retrofit
项目概览
wd1105040417
/
retrofit
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
retrofit
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
a1cee2eb
编写于
11月 25, 2011
作者:
E
Eric Denman
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
JSON request entity support
上级
08396f35
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
535 addition
and
67 deletion
+535
-67
README.markdown
README.markdown
+49
-2
modules/http/src-tests/retrofit/http/HttpRequestBuilderTest.java
.../http/src-tests/retrofit/http/HttpRequestBuilderTest.java
+228
-0
modules/http/src-tests/retrofit/http/RestAdapterTest.java
modules/http/src-tests/retrofit/http/RestAdapterTest.java
+4
-3
modules/http/src/retrofit/http/CallbackResponseHandler.java
modules/http/src/retrofit/http/CallbackResponseHandler.java
+3
-2
modules/http/src/retrofit/http/GsonProvider.java
modules/http/src/retrofit/http/GsonProvider.java
+22
-0
modules/http/src/retrofit/http/GsonResponseHandler.java
modules/http/src/retrofit/http/GsonResponseHandler.java
+3
-2
modules/http/src/retrofit/http/Headers.java
modules/http/src/retrofit/http/Headers.java
+2
-2
modules/http/src/retrofit/http/HttpMethodType.java
modules/http/src/retrofit/http/HttpMethodType.java
+29
-12
modules/http/src/retrofit/http/HttpRequestBuilder.java
modules/http/src/retrofit/http/HttpRequestBuilder.java
+127
-44
modules/http/src/retrofit/http/SingleEntity.java
modules/http/src/retrofit/http/SingleEntity.java
+16
-0
modules/http/src/retrofit/http/TypedBytesEntity.java
modules/http/src/retrofit/http/TypedBytesEntity.java
+51
-0
modules/io/src/retrofit/io/MimeType.java
modules/io/src/retrofit/io/MimeType.java
+1
-0
未找到文件。
README.markdown
浏览文件 @
a1cee2eb
# Retrofit
Reusable Java and Android code from Square, Inc.
Reusable Java and Android code from Square, Inc.
Note that in order to make IntelliJ happy, you'll need to run an ant build (to download all the dependencies).
Modules:
+
IO: Utility classes for doing low-level java I/O.
+
Http: Abstracts away the messy logic of making network calls (depends on IO).
+
Core: Some interfaces and utilities used by the other retrofit modules.
+
Android: Contains two Android-specific utility classes: ShakeDetector (for detecting device
shakes) and QueueFile (for storing a queue on the android file-system).
Note that IntelliJ will complain about compilation errors until you run
`ant`
(to download all the
dependencies). This command also generates the .jar files that you'll want to include in your
application.
## Http Usage
Create an interface for your API. You can create as many of these interfaces as you like. For
each interface you create, calling
`RestAdapter.service(MyInterface.class)`
will create an
instance of that API handler, which you can then store and use throughout your application. An
example interface:
public interface DummyService {
// Produces a url like "foo/bar?id=idValue".
@GET("foo/bar")
void normalGet(@Named("id") String id, Callback<SimpleResponse> callback);
// Produces a url like "foo/idValue/bar?category=categoryValue".
@GET("foo/{id}/bar")
void getWithPathParam(@Named("id") String id, @Named("category") String category, Callback<SimpleResponse> callback);
// Produces a url like "foo/bar/idValue" and body like "id=idValue&body=bodyValue".
@POST("foo/bar/{id}")
void normalPost(@Named("id") String id, @Named("body") String body, Callback<SimpleResponse> callback);
// Produces a url like "foo/bar/idValue" and body generated by MyJsonObj.
@POST("foo/bar/{id}")
void singleEntityPost(@SingleEntity MyJsonObj card, @Named("id") String id, Callback<SimpleResponse> callback);
}
Note that each method _must_ have a Callback object at the end of the parameter list. This is how
your application will handle the results of your network calls: errors and successful responses are
both handled by the Callback interface.
If you want to use the @SingleEntity method of specifying request body (see singleEntityPost above),
your MyJsonObject will need to implement TypedBytes. For convenience, you can extend
\G
sonRequestEntity if you're just trying to send a JSON string in the request body.
Also worth noting: for POST/PUT requests using default form encoding for the request entity (see
normalPost), any path parameters are also included in the request body. This is different from the
behavior of GET/DELETE, where path parameters are excluded from the query string.
\ No newline at end of file
modules/http/src-tests/retrofit/http/HttpRequestBuilderTest.java
0 → 100644
浏览文件 @
a1cee2eb
// Copyright 2011 Square, Inc.
package
retrofit.http
;
import
com.google.inject.name.Named
;
import
java.io.ByteArrayOutputStream
;
import
java.lang.reflect.Method
;
import
java.net.URISyntaxException
;
import
java.util.Set
;
import
java.util.UUID
;
import
junit.framework.TestCase
;
import
org.apache.http.HttpMessage
;
import
org.apache.http.client.methods.HttpGet
;
import
org.apache.http.client.methods.HttpPut
;
import
org.apache.http.client.methods.HttpUriRequest
;
import
retrofit.core.Callback
;
/** @author Eric Denman (edenman@squareup.com) */
public
class
HttpRequestBuilderTest
extends
TestCase
{
public
static
final
String
API_URL
=
"http://taqueria.com/lengua/taco"
;
public
static
final
Headers
BLANK_HEADERS
=
new
Headers
()
{
@Override
public
void
setOn
(
HttpMessage
message
,
String
mimeType
)
{
}
};
public
void
testRegex
()
throws
Exception
{
expectParams
(
""
);
expectParams
(
"foo"
);
expectParams
(
"foo/bar"
);
expectParams
(
"foo/bar/{taco}"
,
"taco"
);
expectParams
(
"foo/bar/{t}"
,
"t"
);
expectParams
(
"foo/bar/{taco}/or/{burrito}"
,
"taco"
,
"burrito"
);
expectParams
(
"foo/bar/{taco}/or/{taco}"
,
"taco"
);
expectParams
(
"foo/bar/{taco-shell}"
,
"taco-shell"
);
expectParams
(
"foo/bar/{taco_shell}"
,
"taco_shell"
);
}
private
void
expectParams
(
String
path
,
String
...
expected
)
{
Set
<
String
>
calculated
=
HttpRequestBuilder
.
getPathParameters
(
path
);
assertEquals
(
expected
.
length
,
calculated
.
size
());
for
(
String
val
:
expected
)
{
assertTrue
(
calculated
.
contains
(
val
));
}
}
public
void
testNormalGet
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"normalGet"
,
String
.
class
,
Callback
.
class
);
String
expectedId
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
expectedId
,
new
MyCallback
()};
HttpUriRequest
request
=
build
(
method
,
args
);
assertTrue
(
request
instanceof
HttpGet
);
HttpGet
put
=
(
HttpGet
)
request
;
// Make sure the url param got translated.
final
String
uri
=
put
.
getURI
().
toString
();
assertEquals
(
API_URL
+
"/foo/bar?id="
+
expectedId
,
uri
);
}
public
void
testGetWithPathParam
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"getWithPathParam"
,
String
.
class
,
String
.
class
,
Callback
.
class
);
String
expectedId
=
UUID
.
randomUUID
().
toString
();
String
category
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
expectedId
,
category
,
new
MyCallback
()};
HttpUriRequest
request
=
build
(
method
,
args
);
assertTrue
(
request
instanceof
HttpGet
);
HttpGet
put
=
(
HttpGet
)
request
;
// Make sure the url param got translated.
final
String
uri
=
put
.
getURI
().
toString
();
assertEquals
(
API_URL
+
"/foo/"
+
expectedId
+
"/bar?category="
+
category
,
uri
);
}
public
void
testSingleEntityWithPathParams
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"singleEntityPut"
,
MyJsonObj
.
class
,
String
.
class
,
Callback
.
class
);
String
expectedId
=
UUID
.
randomUUID
().
toString
();
String
bodyText
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
new
MyJsonObj
(
bodyText
),
expectedId
,
new
MyCallback
()};
HttpUriRequest
request
=
build
(
method
,
args
);
assertTrue
(
request
instanceof
HttpPut
);
HttpPut
put
=
(
HttpPut
)
request
;
// Make sure the url param got translated.
final
String
uri
=
put
.
getURI
().
toString
();
assertEquals
(
API_URL
+
"/foo/bar/"
+
expectedId
,
uri
);
// Make sure the request body has the json string.
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
();
put
.
getEntity
().
writeTo
(
out
);
final
String
requestBody
=
out
.
toString
();
assertEquals
(
"{\"bodyText\":\""
+
bodyText
+
"\"}"
,
requestBody
);
}
public
void
testNormalPutWithPathParams
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"normalPut"
,
String
.
class
,
String
.
class
,
Callback
.
class
);
String
expectedId
=
UUID
.
randomUUID
().
toString
();
String
bodyText
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
expectedId
,
bodyText
,
new
MyCallback
()};
HttpUriRequest
request
=
build
(
method
,
args
);
assertTrue
(
request
instanceof
HttpPut
);
HttpPut
put
=
(
HttpPut
)
request
;
// Make sure the url param got translated.
final
String
uri
=
put
.
getURI
().
toString
();
assertEquals
(
API_URL
+
"/foo/bar/"
+
expectedId
,
uri
);
// Make sure the request body has the json string.
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
();
put
.
getEntity
().
writeTo
(
out
);
final
String
requestBody
=
out
.
toString
();
assertEquals
(
"id="
+
expectedId
+
"&body="
+
bodyText
,
requestBody
);
}
public
void
testSingleEntityWithTooManyParams
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"tooManyParams"
,
MyJsonObj
.
class
,
String
.
class
,
String
.
class
,
Callback
.
class
);
String
expectedId
=
UUID
.
randomUUID
().
toString
();
String
bodyText
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
new
MyJsonObj
(
bodyText
),
expectedId
,
"EXTRA"
,
new
MyCallback
()};
try
{
build
(
method
,
args
);
fail
(
"Didn't throw exception with too many params"
);
}
catch
(
IllegalArgumentException
e
)
{
// Expected
}
}
public
void
testSingleEntityWithNoPathParam
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"singleEntityNoPathParam"
,
MyJsonObj
.
class
,
Callback
.
class
);
String
bodyText
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
new
MyJsonObj
(
bodyText
),
new
MyCallback
()};
try
{
build
(
method
,
args
);
fail
(
"Didn't throw exception with too few params"
);
}
catch
(
IllegalArgumentException
e
)
{
// Expected
}
}
public
void
testRegularWithNoPathParam
()
throws
Exception
{
Method
method
=
MyService
.
class
.
getMethod
(
"regularNoPathParam"
,
String
.
class
,
Callback
.
class
);
String
otherParam
=
UUID
.
randomUUID
().
toString
();
Object
[]
args
=
new
Object
[]
{
otherParam
,
new
MyCallback
()};
try
{
build
(
method
,
args
);
fail
(
"Didn't throw exception with too few params"
);
}
catch
(
IllegalArgumentException
e
)
{
// Expected
}
}
@SuppressWarnings
({
"UnusedDeclaration"
})
// Methods are accessed by reflection.
private
static
interface
MyService
{
@GET
(
"foo/bar"
)
void
normalGet
(
@Named
(
"id"
)
String
id
,
Callback
<
SimpleResponse
>
callback
);
@GET
(
"foo/{id}/bar"
)
void
getWithPathParam
(
@Named
(
"id"
)
String
id
,
@Named
(
"category"
)
String
category
,
Callback
<
SimpleResponse
>
callback
);
@PUT
(
"foo/bar/{id}"
)
void
singleEntityPut
(
@SingleEntity
MyJsonObj
card
,
@Named
(
"id"
)
String
id
,
Callback
<
SimpleResponse
>
callback
);
@PUT
(
"foo/bar/{id}"
)
void
normalPut
(
@Named
(
"id"
)
String
id
,
@Named
(
"body"
)
String
body
,
Callback
<
SimpleResponse
>
callback
);
@PUT
(
"foo/bar/{id}"
)
void
tooManyParams
(
@SingleEntity
MyJsonObj
card
,
@Named
(
"id"
)
String
id
,
@Named
(
"extra"
)
String
extraParam
,
Callback
<
SimpleResponse
>
callback
);
@PUT
(
"foo/bar/{id}"
)
void
singleEntityNoPathParam
(
@SingleEntity
MyJsonObj
card
,
Callback
<
SimpleResponse
>
callback
);
@PUT
(
"foo/bar/{id}"
)
void
regularNoPathParam
(
@Named
(
"other"
)
String
other
,
Callback
<
SimpleResponse
>
callback
);
}
private
HttpUriRequest
build
(
Method
method
,
Object
[]
args
)
throws
URISyntaxException
{
return
new
HttpRequestBuilder
().
setMethod
(
method
)
.
setArgs
(
args
)
.
setApiUrl
(
API_URL
)
.
setHeaders
(
BLANK_HEADERS
)
.
build
();
}
private
static
class
MyJsonObj
{
@SuppressWarnings
({
"UnusedDeclaration"
})
// Accessed by json serialization.
private
String
bodyText
;
public
MyJsonObj
(
String
bodyText
)
{
this
.
bodyText
=
bodyText
;
}
}
private
static
class
SimpleResponse
{
}
private
class
MyCallback
implements
Callback
<
SimpleResponse
>
{
@Override
public
void
preInvoke
()
{
}
@Override
public
void
call
(
SimpleResponse
simpleResponse
)
{
}
@Override
public
void
sessionExpired
()
{
}
@Override
public
void
networkError
()
{
}
@Override
public
void
clientError
(
SimpleResponse
response
)
{
}
@Override
public
void
serverError
(
String
message
)
{
}
@Override
public
void
unexpectedError
(
Throwable
t
)
{
}
}
}
modules/http/src-tests/retrofit/http/RestAdapterTest.java
浏览文件 @
a1cee2eb
...
...
@@ -24,7 +24,6 @@ import org.easymock.IAnswer;
import
org.junit.Before
;
import
retrofit.core.Callback
;
import
retrofit.core.MainThread
;
import
retrofit.internal.gson.Gson
;
import
static
org
.
easymock
.
EasyMock
.
capture
;
import
static
org
.
easymock
.
EasyMock
.
createMock
;
...
...
@@ -33,6 +32,7 @@ import static org.easymock.EasyMock.expectLastCall;
import
static
org
.
easymock
.
EasyMock
.
isA
;
import
static
org
.
easymock
.
EasyMock
.
replay
;
import
static
org
.
easymock
.
EasyMock
.
verify
;
import
static
retrofit
.
http
.
GsonProvider
.
gson
;
import
static
retrofit
.
http
.
RestAdapter
.
service
;
public
class
RestAdapterTest
extends
TestCase
{
...
...
@@ -260,7 +260,7 @@ public class RestAdapterTest extends TestCase {
expectExecution
(
mockMainThread
);
// For call()
expectSetOnWithRequest
(
requestClass
,
requestUrl
);
Response
response
=
new
Response
(
"some text"
);
expectResponseCalls
(
new
G
son
().
toJson
(
response
));
expectResponseCalls
(
g
son
().
toJson
(
response
));
expectHttpClientExecute
();
expectCallbacks
(
response
);
}
...
...
@@ -294,7 +294,8 @@ public class RestAdapterTest extends TestCase {
private
<
T
extends
HttpUriRequest
>
void
expectSetOnWithRequest
(
final
Class
<
T
>
expectedRequestClass
,
final
String
expectedUri
)
{
final
Capture
<
HttpMessage
>
capture
=
new
Capture
<
HttpMessage
>();
mockHeaders
.
setOn
(
capture
(
capture
));
final
Capture
<
String
>
captureMime
=
new
Capture
<
String
>();
mockHeaders
.
setOn
(
capture
(
capture
),
capture
(
captureMime
));
expectLastCall
().
andAnswer
(
new
IAnswer
<
Object
>()
{
@Override
public
Object
answer
()
throws
Throwable
{
T
request
=
expectedRequestClass
.
cast
(
capture
.
getValue
());
...
...
modules/http/src/retrofit/http/CallbackResponseHandler.java
浏览文件 @
a1cee2eb
...
...
@@ -10,7 +10,8 @@ import org.apache.http.StatusLine;
import
org.apache.http.client.ResponseHandler
;
import
org.apache.http.entity.BufferedHttpEntity
;
import
retrofit.core.Callback
;
import
retrofit.internal.gson.Gson
;
import
static
retrofit
.
http
.
GsonProvider
.
gson
;
/**
* Support for response handlers that invoke {@link Callback}.
...
...
@@ -138,7 +139,7 @@ public abstract class CallbackResponseHandler<T>
if
(
statusCode
==
BAD_GATEWAY
||
statusCode
==
GATEWAY_TIMEOUT
||
statusCode
<
500
)
{
try
{
ServerError
serverError
=
new
G
son
().
fromJson
(
body
,
ServerError
.
class
);
ServerError
serverError
=
g
son
().
fromJson
(
body
,
ServerError
.
class
);
if
(
serverError
!=
null
)
return
serverError
.
message
;
}
catch
(
Throwable
t
)
{
// The server error takes precedence.
...
...
modules/http/src/retrofit/http/GsonProvider.java
0 → 100644
浏览文件 @
a1cee2eb
// Copyright 2011 Square, Inc.
package
retrofit.http
;
import
retrofit.internal.gson.Gson
;
/**
* Creating a Gson instance is relatively expensive (70ms on my MBP), and Gson is thread-safe.
* Create the instance once so we're not constantly creating them.
*
* @author Eric Denman (edenman@squareup.com)
*/
public
class
GsonProvider
{
/** Lazily loads the Gson instance. */
private
static
class
Holder
{
static
final
Gson
gson
=
new
Gson
();
}
public
static
Gson
gson
()
{
return
Holder
.
gson
;
}
}
modules/http/src/retrofit/http/GsonResponseHandler.java
浏览文件 @
a1cee2eb
...
...
@@ -7,9 +7,10 @@ import java.util.logging.Level;
import
java.util.logging.Logger
;
import
org.apache.http.HttpEntity
;
import
retrofit.core.Callback
;
import
retrofit.internal.gson.Gson
;
import
retrofit.internal.gson.JsonParseException
;
import
static
retrofit
.
http
.
GsonProvider
.
gson
;
/**
* Converts JSON response to an object using Gson and then passes it to {@link
* Callback#call(T)}.
...
...
@@ -46,7 +47,7 @@ class GsonResponseHandler<T> extends CallbackResponseHandler<T> {
* We derived type from Callback<T>, so we know we're safe.
*/
@SuppressWarnings
(
"unchecked"
)
T
t
=
(
T
)
new
G
son
().
fromJson
(
in
,
type
);
T
t
=
(
T
)
g
son
().
fromJson
(
in
,
type
);
return
t
;
}
catch
(
JsonParseException
e
)
{
// The server returned us bad JSON!
...
...
modules/http/src/retrofit/http/Headers.java
浏览文件 @
a1cee2eb
...
...
@@ -10,6 +10,6 @@ import org.apache.http.HttpMessage;
*/
public
interface
Headers
{
/** Sets headers on the given message
.
*/
public
void
setOn
(
HttpMessage
message
);
/** Sets headers on the given message
, with the specified MIME type
*/
public
void
setOn
(
HttpMessage
message
,
String
mimeType
);
}
modules/http/src/retrofit/http/HttpMethodType.java
浏览文件 @
a1cee2eb
...
...
@@ -34,7 +34,7 @@ public enum HttpMethodType {
throws
URISyntaxException
{
URI
uri
=
getParameterizedUri
(
builder
);
HttpGet
request
=
new
HttpGet
(
uri
);
builder
.
getHeaders
().
setOn
(
request
);
builder
.
getHeaders
().
setOn
(
request
,
builder
.
getMimeType
()
);
return
request
;
}
},
...
...
@@ -45,7 +45,7 @@ public enum HttpMethodType {
URI
uri
=
getUri
(
builder
);
HttpPost
request
=
new
HttpPost
(
uri
);
addParams
(
request
,
builder
);
builder
.
getHeaders
().
setOn
(
request
);
builder
.
getHeaders
().
setOn
(
request
,
builder
.
getMimeType
()
);
return
request
;
}
},
...
...
@@ -56,7 +56,7 @@ public enum HttpMethodType {
URI
uri
=
getUri
(
builder
);
HttpPut
request
=
new
HttpPut
(
uri
);
addParams
(
request
,
builder
);
builder
.
getHeaders
().
setOn
(
request
);
builder
.
getHeaders
().
setOn
(
request
,
builder
.
getMimeType
()
);
return
request
;
}
},
...
...
@@ -66,7 +66,7 @@ public enum HttpMethodType {
throws
URISyntaxException
{
URI
uri
=
getParameterizedUri
(
builder
);
HttpDelete
request
=
new
HttpDelete
(
uri
);
builder
.
getHeaders
().
setOn
(
request
);
builder
.
getHeaders
().
setOn
(
request
,
builder
.
getMimeType
()
);
return
request
;
}
};
...
...
@@ -92,9 +92,8 @@ public enum HttpMethodType {
throws
URISyntaxException
{
List
<
NameValuePair
>
queryParams
=
builder
.
getParamList
(
false
);
String
queryString
=
URLEncodedUtils
.
format
(
queryParams
,
"UTF-8"
);
URI
uri
=
URIUtils
.
createURI
(
builder
.
getScheme
(),
builder
.
getHost
(),
-
1
,
builder
.
getRelativePath
(),
queryString
,
null
);
return
uri
;
return
URIUtils
.
createURI
(
builder
.
getScheme
(),
builder
.
getHost
(),
-
1
,
builder
.
getRelativePath
(),
queryString
,
null
);
}
/**
...
...
@@ -111,7 +110,7 @@ public enum HttpMethodType {
method
.
getParameterAnnotations
();
int
count
=
parameterAnnotations
.
length
-
1
;
if
(
useMultipart
(
parameterTypes
))
{
if
(
useMultipart
(
parameterTypes
,
parameterAnnotations
))
{
MultipartEntity
form
=
new
MultipartEntity
(
HttpMultipartMode
.
BROWSER_COMPATIBLE
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
...
...
@@ -135,9 +134,15 @@ public enum HttpMethodType {
request
.
setEntity
(
form
);
}
else
{
try
{
List
<
NameValuePair
>
paramList
=
builder
.
getParamList
(
true
);
if
(
builder
.
getSingleEntity
()
!=
null
)
{
final
TypedBytesEntity
entity
=
new
TypedBytesEntity
(
builder
.
getSingleEntity
());
request
.
setEntity
(
entity
);
request
.
addHeader
(
"Content-Type"
,
entity
.
getMimeType
().
mimeName
());
}
else
{
List
<
NameValuePair
>
paramList
=
builder
.
getParamList
(
true
);
// TODO: Use specified encoding. (See CallbackResponseHandler et al)
request
.
setEntity
(
new
UrlEncodedFormEntity
(
paramList
,
HTTP
.
UTF_8
));
}
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
AssertionError
(
e
);
}
...
...
@@ -145,9 +150,21 @@ public enum HttpMethodType {
}
/** Returns true if the parameters contain a file upload. */
private
static
boolean
useMultipart
(
Class
<?>[]
parameterTypes
)
{
for
(
Class
<?>
parameterType
:
parameterTypes
)
{
if
(
TypedBytes
.
class
.
isAssignableFrom
(
parameterType
))
return
true
;
private
static
boolean
useMultipart
(
Class
<?>[]
parameterTypes
,
Annotation
[][]
parameterAnnotations
)
{
for
(
int
i
=
0
;
i
<
parameterTypes
.
length
;
i
++)
{
Class
<?>
parameterType
=
parameterTypes
[
i
];
Annotation
[]
annotations
=
parameterAnnotations
[
i
];
if
(
TypedBytes
.
class
.
isAssignableFrom
(
parameterType
)
&&
!
hasSingleEntityAnnotation
(
annotations
))
{
return
true
;
}
}
return
false
;
}
private
static
boolean
hasSingleEntityAnnotation
(
Annotation
[]
annotations
)
{
for
(
Annotation
annotation
:
annotations
)
{
if
(
annotation
.
annotationType
().
equals
(
SingleEntity
.
class
))
return
true
;
}
return
false
;
}
...
...
modules/http/src/retrofit/http/HttpRequestBuilder.java
浏览文件 @
a1cee2eb
package
retrofit.http
;
import
com.google.inject.name.Named
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.Method
;
import
java.net.URISyntaxException
;
import
java.util.ArrayList
;
import
java.util.
Iterator
;
import
java.util.
HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.logging.Level
;
import
java.util.logging.Logger
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
org.apache.http.NameValuePair
;
import
org.apache.http.client.methods.HttpUriRequest
;
import
org.apache.http.message.BasicNameValuePair
;
import
retrofit.io.MimeType
;
import
retrofit.io.TypedBytes
;
import
static
retrofit
.
http
.
GsonProvider
.
gson
;
/**
* Builds HTTP requests from Java method invocations. Handles "path parameters"
...
...
@@ -25,8 +34,7 @@ import org.apache.http.message.BasicNameValuePair;
* </ol>
*/
final
class
HttpRequestBuilder
{
private
static
final
Logger
logger
=
Logger
.
getLogger
(
HttpRequestBuilder
.
class
.
getName
());
private
static
final
Logger
logger
=
Logger
.
getLogger
(
HttpRequestBuilder
.
class
.
getName
());
private
Method
javaMethod
;
private
Object
[]
args
;
...
...
@@ -35,6 +43,7 @@ final class HttpRequestBuilder {
private
Headers
headers
;
private
List
<
NameValuePair
>
nonPathParams
;
private
RequestLine
requestLine
;
private
TypedBytes
singleEntity
;
HttpRequestBuilder
setMethod
(
Method
method
)
{
this
.
javaMethod
=
method
;
...
...
@@ -47,12 +56,7 @@ final class HttpRequestBuilder {
}
String
getRelativePath
()
{
return
replacedRelativePath
!=
null
?
replacedRelativePath
:
requestLine
.
getRelativePath
();
}
private
boolean
hasPathParameters
()
{
return
requestLine
.
getRelativePath
().
contains
(
"{"
);
return
replacedRelativePath
!=
null
?
replacedRelativePath
:
requestLine
.
getRelativePath
();
}
HttpRequestBuilder
setApiUrl
(
String
apiUrl
)
{
...
...
@@ -84,29 +88,24 @@ final class HttpRequestBuilder {
}
String
getHost
()
{
String
host
=
apiUrl
.
substring
(
apiUrl
.
indexOf
(
"://"
)
+
3
,
apiUrl
.
length
());
String
host
=
apiUrl
.
substring
(
apiUrl
.
indexOf
(
"://"
)
+
3
,
apiUrl
.
length
());
if
(
host
.
endsWith
(
"/"
))
host
=
host
.
substring
(
0
,
host
.
length
()
-
1
);
return
host
;
}
/**
* Converts all but the last method argument to a list of HTTP request
*
parameters. If includePathParams is true, path parameters (like id in
*
"/entity/{id}" will be included in this
list.
* Converts all but the last method argument to a list of HTTP request
parameters. If
*
includePathParams is true, path parameters (like id in "/entity/{id}" will be included in this
* list.
*/
List
<
NameValuePair
>
getParamList
(
boolean
includePathParams
)
{
if
(
includePathParams
||
nonPathParams
==
null
)
return
createParamList
();
return
nonPathParams
;
}
/**
* Converts all but the last method argument to a list of HTTP request
* parameters.
*/
/** Converts all but the last method argument to a list of HTTP request parameters. */
private
List
<
NameValuePair
>
createParamList
()
{
Annotation
[][]
parameterAnnotations
=
javaMethod
.
getParameterAnnotations
();
Annotation
[][]
parameterAnnotations
=
javaMethod
.
getParameterAnnotations
();
int
count
=
parameterAnnotations
.
length
-
1
;
List
<
NameValuePair
>
params
=
new
ArrayList
<
NameValuePair
>(
count
);
...
...
@@ -130,48 +129,107 @@ final class HttpRequestBuilder {
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
Object
arg
=
args
[
i
];
if
(
arg
==
null
)
continue
;
String
name
=
getName
(
parameterAnnotations
[
i
],
javaMethod
,
i
);
params
.
add
(
new
BasicNameValuePair
(
name
,
String
.
valueOf
(
arg
)));
for
(
Annotation
annotation
:
parameterAnnotations
[
i
])
{
final
Class
<?
extends
Annotation
>
type
=
annotation
.
annotationType
();
if
(
type
==
Named
.
class
)
{
String
name
=
getName
(
parameterAnnotations
[
i
],
javaMethod
,
i
);
params
.
add
(
new
BasicNameValuePair
(
name
,
String
.
valueOf
(
arg
)));
}
else
if
(
type
==
SingleEntity
.
class
)
{
if
(
arg
instanceof
TypedBytes
)
{
// Let the object specify its own entity representation.
singleEntity
=
(
TypedBytes
)
arg
;
}
else
{
// Just an object: serialize it with json
singleEntity
=
new
JsonTypedBytes
(
arg
);
}
}
}
}
return
params
;
}
public
TypedBytes
getSingleEntity
()
{
return
singleEntity
;
}
/**
* If this builder has a custom mime-type for the request, this returns it.
*
* @return "Content-Type" string if present, null otherwise.
*/
public
String
getMimeType
()
{
return
singleEntity
==
null
?
null
:
singleEntity
.
mimeType
().
mimeName
();
}
private
BasicNameValuePair
addPair
(
QueryParam
queryParam
)
{
return
new
BasicNameValuePair
(
queryParam
.
name
(),
queryParam
.
value
());
}
HttpUriRequest
build
()
throws
URISyntaxException
{
// Alter parameter list if path parameters are present.
if
(
hasPathParameters
())
{
List
<
NameValuePair
>
paramList
=
createParamList
();
Set
<
String
>
pathParams
=
getPathParameters
(
requestLine
.
getRelativePath
());
List
<
NameValuePair
>
paramList
=
createParamList
();
if
(!
pathParams
.
isEmpty
())
{
String
replacedPath
=
requestLine
.
getRelativePath
();
Iterator
<
NameValuePair
>
itor
=
paramList
.
iterator
();
while
(
itor
.
hasNext
())
{
NameValuePair
pair
=
itor
.
next
();
String
paramName
=
pair
.
getName
();
if
(
replacedPath
.
contains
(
"{"
+
paramName
+
"}"
))
{
replacedPath
=
replacedPath
.
replaceAll
(
"\\{"
+
paramName
+
"\\}"
,
pair
.
getValue
());
itor
.
remove
();
for
(
String
pathParam
:
pathParams
)
{
NameValuePair
found
=
null
;
for
(
NameValuePair
param
:
paramList
)
{
if
(
param
.
getName
().
equals
(
pathParam
))
{
found
=
param
;
}
}
if
(
found
!=
null
)
{
replacedPath
=
doReplace
(
replacedPath
,
found
.
getName
(),
found
.
getValue
());
paramList
.
remove
(
found
);
}
else
{
throw
new
IllegalArgumentException
(
"Got pathParam "
+
pathParam
+
" that wasn't specified with @Named param."
);
}
}
replacedRelativePath
=
replacedPath
;
nonPathParams
=
paramList
;
}
if
(
getSingleEntity
()
!=
null
)
{
// We're passing a JSON object as the main entity: paramList should only contain path
// parameter values.
if
(!
paramList
.
isEmpty
())
{
throw
new
IllegalArgumentException
(
"Found @Named param on single-entity request that "
+
"wasn't used for path substitution: this shouldn't be on the method."
);
}
}
HttpUriRequest
request
=
requestLine
.
getHttpMethod
().
createFrom
(
this
);
if
(
logger
.
isLoggable
(
Level
.
FINE
))
logger
.
fine
(
"Request params: "
+
getParamList
(
true
));
if
(
logger
.
isLoggable
(
Level
.
FINE
))
logger
.
fine
(
"Request params: "
+
getParamList
(
true
));
return
request
;
}
private
String
doReplace
(
String
replacedPath
,
String
paramName
,
String
newVal
)
{
replacedPath
=
replacedPath
.
replaceAll
(
"\\{"
+
paramName
+
"\\}"
,
newVal
);
return
replacedPath
;
}
/**
* Gets the set of unique path params used in the given uri. If a param is used twice in the uri,
* it will only show up once in the set.
*
* @param path the path to search through.
* @return set of path params.
*/
static
Set
<
String
>
getPathParameters
(
String
path
)
{
Pattern
p
=
Pattern
.
compile
(
"\\{([a-z_-]*)\\}"
);
Matcher
m
=
p
.
matcher
(
path
);
Set
<
String
>
patterns
=
new
HashSet
<
String
>();
while
(
m
.
find
())
{
patterns
.
add
(
m
.
group
(
1
));
}
return
patterns
;
}
/** Gets the parameter name from the @Named annotation. */
static
String
getName
(
Annotation
[]
annotations
,
Method
method
,
int
parameterIndex
)
{
return
findAnnotation
(
annotations
,
Named
.
class
,
method
,
parameterIndex
).
value
();
static
String
getName
(
Annotation
[]
annotations
,
Method
method
,
int
parameterIndex
)
{
return
findAnnotation
(
annotations
,
Named
.
class
,
method
,
parameterIndex
).
value
();
}
/**
...
...
@@ -179,15 +237,40 @@ final class HttpRequestBuilder {
*
* @throws IllegalArgumentException if the annotation isn't found
*/
private
static
<
A
extends
Annotation
>
A
findAnnotation
(
Annotation
[]
annotations
,
Class
<
A
>
annotationType
,
Method
method
,
int
parameterIndex
)
{
private
static
<
A
extends
Annotation
>
A
findAnnotation
(
Annotation
[]
annotations
,
Class
<
A
>
annotationType
,
Method
method
,
int
parameterIndex
)
{
for
(
Annotation
annotation
:
annotations
)
{
if
(
annotation
.
annotationType
()
==
annotationType
)
{
return
annotationType
.
cast
(
annotation
);
}
}
throw
new
IllegalArgumentException
(
annotationType
+
" missing on"
+
" parameter #"
+
parameterIndex
+
" of "
+
method
+
"."
);
throw
new
IllegalArgumentException
(
annotationType
+
" missing on"
+
" parameter #"
+
parameterIndex
+
" of "
+
method
+
"."
);
}
private
class
JsonTypedBytes
implements
TypedBytes
{
private
Object
obj
;
public
JsonTypedBytes
(
Object
obj
)
{
this
.
obj
=
obj
;
}
@Override
public
MimeType
mimeType
()
{
return
MimeType
.
JSON
;
}
@Override
public
int
length
()
{
return
toJson
().
length
();
}
@Override
public
void
writeTo
(
OutputStream
out
)
throws
IOException
{
final
String
str
=
toJson
();
// TODO use requested encoding?
out
.
write
(
str
.
getBytes
(
"UTF-8"
));
}
protected
String
toJson
()
{
return
gson
().
toJson
(
obj
);
}
}
}
\ No newline at end of file
modules/http/src/retrofit/http/SingleEntity.java
0 → 100644
浏览文件 @
a1cee2eb
// Copyright 2011 Square, Inc.
package
retrofit.http
;
/**
* Use this annotation on a service method param when you want to directly control the request body
* of a POST/PUT request (instead of sending in as request parameters or form-style request
* body). If the value of the parameter implements TypedBytes, the request body will be written
* exactly as specified by the TypedBytes.writeTo object. If it doesn't implement TypedBytes, the
* object will be serialized into JSON and the result will be set directly as the request body.
*
* @author Eric Denman (edenman@squareup.com)
*/
@java
.
lang
.
annotation
.
Retention
(
java
.
lang
.
annotation
.
RetentionPolicy
.
RUNTIME
)
@java
.
lang
.
annotation
.
Target
(
java
.
lang
.
annotation
.
ElementType
.
PARAMETER
)
public
@interface
SingleEntity
{
}
modules/http/src/retrofit/http/TypedBytesEntity.java
0 → 100644
浏览文件 @
a1cee2eb
// Copyright 2011 Square, Inc.
package
retrofit.http
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
org.apache.http.entity.AbstractHttpEntity
;
import
retrofit.io.MimeType
;
import
retrofit.io.TypedBytes
;
/**
* Container class for when you want to pass an entire TypedBytes as a http request entity.
*
* @author Eric Denman (edenman@squareup.com)
*/
public
class
TypedBytesEntity
extends
AbstractHttpEntity
{
private
TypedBytes
typedBytes
;
public
TypedBytesEntity
(
TypedBytes
typedBytes
)
{
this
.
typedBytes
=
typedBytes
;
}
@Override
public
boolean
isRepeatable
()
{
return
true
;
}
@Override
public
long
getContentLength
()
{
return
typedBytes
.
length
();
}
@Override
public
InputStream
getContent
()
throws
IOException
,
IllegalStateException
{
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
();
typedBytes
.
writeTo
(
out
);
return
new
ByteArrayInputStream
(
out
.
toByteArray
());
}
@Override
public
void
writeTo
(
OutputStream
out
)
throws
IOException
{
typedBytes
.
writeTo
(
out
);
}
@Override
public
boolean
isStreaming
()
{
return
false
;
}
public
MimeType
getMimeType
()
{
return
typedBytes
.
mimeType
();
}
}
modules/io/src/retrofit/io/MimeType.java
浏览文件 @
a1cee2eb
...
...
@@ -8,6 +8,7 @@ package retrofit.io;
*/
public
enum
MimeType
{
JSON
(
"application/json"
,
"json"
),
GIF
(
"image/gif"
,
"gif"
),
PNG
(
"image/png"
,
"png"
),
JPEG
(
"image/jpeg"
,
"jpg"
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录