基于Jersey利用groovy的extensionModule特性为String类扩展一些实用的Http方法来方便编写简单的爬虫同时对调用者屏蔽jersey的api来简化操作支持getpostpost+jsonuploaddownload修改请求头拦截器等多种方法
。
首先,我们需要在项目中引入Jersey和Groovy的依赖:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.26</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
接着,我们创建一个Groovy类来扩展String类的功能:
import javax.ws.rs.client.ClientBuilder
import javax.ws.rs.client.Entity
import javax.ws.rs.client.WebTarget
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.util.Map
class HttpExtensionModule {
def static extensionMap = [:]
static {
extensionMap['get'] = { Map<String, Object> headers = [:] ->
def builder = ClientBuilder.newBuilder()
def client = builder.build()
def target = client.target(this)
headers.each { key, value ->
target = target.request().header(key, value)
}
target.request(MediaType.APPLICATION_JSON_TYPE).get()
}
extensionMap['post'] = { Map<String, Object> headers = [:], String body = '' ->
def builder = ClientBuilder.newBuilder()
def client = builder.build()
def target = client.target(this)
headers.each { key, value ->
target = target.request().header(key, value)
}
target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.entity(body, MediaType.APPLICATION_JSON_TYPE))
}
extensionMap['postJson'] = { Map<String, Object> headers = [:], String json = '' ->
def builder = ClientBuilder.newBuilder()
def client = builder.build()
def target = client.target(this)
headers.each { key, value ->
target = target.request().header(key, value)
}
target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(json))
}
extensionMap['upload'] = { Map<String, Object> headers = [:], String fieldName, File file ->
def builder = ClientBuilder.newBuilder()
def client = builder.build()
def target = client.target(this)
headers.each { key, value ->
target = target.request().header(key, value)
}
target.request(MediaType.MULTIPART_FORM_DATA_TYPE)
.post(Entity.entity(new FormDataMultiPart().field(fieldName, file, MediaType.APPLICATION_OCTET_STREAM_TYPE), MediaType.MULTIPART_FORM_DATA_TYPE))
}
extensionMap['download'] = { Map<String, Object> headers = [:], String filePath ->
def builder = ClientBuilder.newBuilder()
def client = builder.build()
def target = client.target(this)
headers.each { key, value ->
target = target.request().header(key, value)
}
def response = target.request().get()
def inputStream = response.readEntity(InputStream)
def outputStream = new FileOutputStream(new File(filePath))
byte[] buffer = new byte[1024]
int len
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len)
}
outputStream.close()
response.close()
}
}
def static interceptor = { String methodName, List args ->
def target = args[0]
def method = extensionMap[methodName]
method.curry(target).curry(args[1..-1])
}
}
这里我们扩展了五个方法:get、post、postJson、upload和download。这些方法都接受一个Map类型的参数headers,用于设置请求头;另外,post和postJson方法还接受一个String类型的参数body或json,用于设置请求体。upload方法接受三个参数:headers、fieldName和file,用于上传文件。download方法接受两个参数:headers和filePath,用于下载文件。
注意,我们在这个类中定义了一个extensionMap变量,它是一个静态变量,用于存储扩展的方法。我们还定义了一个interceptor变量,它是一个静态方法,用于拦截调用String类的方法,如果该方法是我们扩展的方法之一,则返回一个闭包,该闭包会调用我们扩展的方法,并传递相应的参数。
接下来,我们需要实现一个Jersey的ContainerRequestFilter,用于在请求到达服务器之前,将String类型的参数注入到请求中。这里我们使用了一个叫做StringInjector的类,它实现了ValueFactory接口:
import javax.inject.Singleton;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
@Provider
@Singleton
public class StringInjector implements ValueFactory<String> {
@Context
private Providers providers;
public String provide() {
ContainerRequestContext requestContext = RequestContext.getCurrent().getRequestContext();
String contentType = requestContext.getHeaderString("Content-Type");
MediaType mediaType = MediaType.valueOf(contentType);
if (mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
return requestContext.getEntityStream().getText();
} else {
MultivaluedMap<String, String> formParams = new MultivaluedHashMap<>();
providers.getContextResolver(FormProvider.class, MediaType.APPLICATION_FORM_URLENCODED_TYPE).getContext(null).readFrom(
MultivaluedHashMap.class, null, null, null, requestContext.getEntityStream(), formParams);
return formParams.getFirst("data");
}
}
@Provider
public static class StringInjectorResolver implements ContextResolver<ValueFactory<String>> {
@Override
public ValueFactory<String> getContext(Class<?> type) {
if (type == String.class) {
return new StringInjector();
} else {
return null;
}
}
}
}
在这个类中,我们首先获取请求头中的Content-Type,然后根据不同的MediaType,从请求实体中获取字符串。注意,这里我们使用了一个叫做FormProvider的类,它实现了MessageBodyReader接口,用于将表单数据转换为MultivaluedMap。我们还定义了一个叫做StringInjectorResolver的类,它实现了ContextResolver接口,用于返回StringInjector实例。
最后,我们需要在Jersey的Application中注册StringInjectorResolver和HttpExtensionModule:
import org.glassfish.jersey.server.ResourceConfig;
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(StringInjector.StringInjectorResolver.class);
register(HttpExtensionModule.class);
}
}
现在,我们就可以在代码中愉快地使用我们扩展的方法了:
import static HttpExtensionModule.*
@Grab('com.google.code.gson:gson:2.8.7')
import com.google.gson.Gson
def url = 'https://httpbin.org/post'
def headers = ['User-Agent': 'Mozilla/5.0']
// 发送POST请求,设置请求头和请求体
def response = url.post(headers, new Gson().toJson([name: 'John', age: 25]))
assert response.status == 200
// 发送POST请求,设置请求头和JSON请求体
response = url.postJson(headers, new Gson().toJson([name: 'John', age: 25]))
assert response.status == 200
// 发送GET请求,设置请求头
response = url.get(headers)
assert response.status == 200
// 上传文件,设置请求头和文件名
response = url.upload(headers, 'file', new File('test.txt'))
assert response.status == 200
// 下载文件,设置请求头和文件路径
url.download(headers, 'test.txt')
``
原文地址: https://www.cveoy.top/t/topic/gLqt 著作权归作者所有。请勿转载和采集!