首先,我们需要在项目中引入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')
``
基于Jersey利用groovy的extensionModule特性为String类扩展一些实用的Http方法来方便编写简单的爬虫同时对调用者屏蔽jersey的api来简化操作支持getpostpost+jsonuploaddownload修改请求头拦截器等多种方法

原文地址: https://www.cveoy.top/t/topic/gLqt 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录