改造请求正文:必填字段

时间:2017-04-18 14:53:57

标签: java retrofit retrofit2

使用请求正文的过程在官方API声明页面中描述如下:

@POST("users/new")
Call<User> createUser(@Body User user);

虽然没有创建User对象的指南,但我想它看起来像这样:

public class User {
    public String name;
    public String group;
}

通过扩展,这将导致像这样的请求体:

{ 
    "name": string, 
    "group": string 
}

默认情况下,这些字段似乎是可选的。我需要的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

有很多方法可以实现这种行为。你可以:

  • ...在调用Retrofitted-service(用户输入表单等)之前验证您的对象是POST,并让它快速失败。
  • ...在Retrofit请求转换器中集中验证您的对象并使用链式转换器
  • ...验证您的数据传输对象对象(如果有的话),在从对象转换并准备发送之后进行集中处理
  • ...依赖于服务器API实现而不关心客户端的验证:不需要特别重复服务器逻辑,您可能会与服务器API验证同步,您编写更多代码,这就是我在评论中建议你的。

如果真的需要在发送之前验证请求正文,您应该使用第一个选项。如果要使验证完全集中,可以实现自定义的Retrofit转换器以进行预验证。 (下面的代码使用Java 8和一点点Google Guava,Retrofit 2和Gson,但是它可以很容易地为其他组件重做。)

考虑这些:

interface IService {

    @POST("/")
    Call<String> post(
            @Body User user
    );

}
final class User {

    final String name;
    final String group;

    User(final String name, final String group) {
        this.name = name;
        this.group = group;
    }

}

现在我们可以实施Retrofit-stuff。以下mockOkHttpClient是模拟OkHttpClient,可以使用HTTP 200 OK"OK"消费任何请求并回复。

private static final OkHttpClient mockOkHttpClient = new OkHttpClient.Builder()
        .addInterceptor(chain -> new Response.Builder()
                .request(chain.request())
                .protocol(HTTP_1_0)
                .code(HTTP_OK)
                .body(ResponseBody.create(MediaType.parse("application/json"), "\"OK\""))
                .build()
        )
        .build();

现在让我们做一个简单的测试:

final Iterable<Retrofit> retrofits = ImmutableList.of(
        getAsIsRetrofit(),
        getValidatedDomainObjectsRetrofit(),
        getValidatedDataTransferObjectsRetrofit()
);
final User user = new User("user", "group");
for ( final Retrofit retrofit : retrofits ) {
    final IService service = retrofit.create(IService.class);
    final String message = service.post(user).execute().body();
    System.out.println(message);
}

如您所见,有三个Retrofit实例使用不同的配置进行实例化以演示每个实例。

以下Retrofit实例不关心验证本身。这是我建议您使用的另一个时间:简单地发布您获得的内容并让服务器API实现自己处理它。考虑API实现以返回好的响应,如HTTP 400 Bad Request等。

private static Retrofit getAsIsRetrofit() {
    return new Retrofit.Builder()
            .client(mockOkHttpClient)
            .baseUrl("http://whatever")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

以下Retrofit实例在转换为Gson友好表示之前验证给定的User对象(取决于您是否有域对象进行数据传输应用程序中的对象转换

private static Retrofit getValidatedDomainObjectsRetrofit() {
    return new Retrofit.Builder()
            .client(mockOkHttpClient)
            .baseUrl("http://whatever")
            .addConverterFactory(new Converter.Factory() {
                @Override
                public Converter<?, RequestBody> requestBodyConverter(final Type type, final Annotation[] parameterAnnotations,
                        final Annotation[] methodAnnotations, final Retrofit retrofit) {
                    if ( type != User.class ) {
                        return null;
                    }
                    final Converter<Object, RequestBody> nextConverter = retrofit.nextRequestBodyConverter(this, type, parameterAnnotations, methodAnnotations);
                    return (Converter<Object, RequestBody>) value -> {
                        if ( value instanceof User ) {
                            final User user = (User) value;
                            requireNonNull(user.name, "name must not be null");
                            requireNonNull(user.group, "group must not be null");
                        }
                        return nextConverter.convert(value);
                    };
                }

            })
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

下一个在将数据传输对象写入输出流之前验证它们。可能是这里最低级别的实例。

private static Retrofit getValidatedDataTransferObjectsRetrofit() {
    final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(new TypeAdapterFactory() {
                @Override
                public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                    if ( typeToken.getRawType() != User.class ) {
                        return null;
                    }
                    final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
                    return new TypeAdapter<T>() {
                        @Override
                        public void write(final JsonWriter out, final T value)
                                throws IOException {
                            if ( value instanceof User ) {
                                final User user = (User) value;
                                requireNonNull(user.name, "name must not be null");
                                requireNonNull(user.group, "group must not be null");
                            }
                            delegateTypeAdapter.write(out, value);
                        }

                        @Override
                        public T read(final JsonReader in)
                                throws IOException {
                            return delegateTypeAdapter.read(in);
                        }
                    };
                }
            })
            .create();
    return new Retrofit.Builder()
            .client(mockOkHttpClient)
            .baseUrl("http://whatever")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();
}

请注意requireNonNull是JDK 8方法,如果你想要@NotNull之类的东西,你可以实现自己的注释处理器,或者在互联网上找到这样的实现,因为我的实现思路没用。 :)但是,我认为你最喜欢的是as-is方法。