001/* 002 * Copyright 2022-2025 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.soklet.core; 018 019import javax.annotation.Nonnull; 020import java.lang.reflect.Parameter; 021import java.lang.reflect.Type; 022import java.util.Optional; 023 024/** 025 * Contract for converting request body bytes into a corresponding Java type. 026 * <p> 027 * For example, if your <em>Resource Methods</em> expect JSON request bodies like this (note the {@link com.soklet.annotation.RequestBody} annotation): 028 * <pre>{@code @POST("/find-biggest") 029 * public Integer findBiggest(@RequestBody List<Integer> numbers) { 030 * // JSON request body [1,2,3] results in 3 being returned 031 * return Collections.max(numbers); 032 * }}</pre> 033 * <p> 034 * You might implement a {@link RequestBodyMarshaler} to accept JSON like this: 035 * <pre>{@code SokletConfiguration config = SokletConfiguration.withServer( 036 * DefaultServer.withPort(8080).build() 037 * ).requestBodyMarshaler(new RequestBodyMarshaler() { 038 * // This example uses Google's GSON 039 * static final Gson GSON = new Gson(); 040 * 041 * @Nonnull 042 * @Override 043 * public Optional<Object> marshalRequestBody( 044 * @Nonnull Request request, 045 * @Nonnull ResourceMethod resourceMethod, 046 * @Nonnull Parameter parameter, 047 * @Nonnull Type requestBodyType 048 * ) { 049 * // Let GSON turn the request body into an instance 050 * // of the specified type. 051 * // 052 * // Note that this method has access to all runtime information 053 * // about the request, which provides the opportunity to, for example, 054 * // examine annotations on the method/parameter which might 055 * // inform custom marshaling strategies. 056 * return Optional.of(GSON.fromJson( 057 * request.getBodyAsString().get(), 058 * requestBodyType 059 * )); 060 * } 061 * }).build();}</pre> 062 * <p> 063 * See <a href="https://www.soklet.com/docs/request-handling#request-body">https://www.soklet.com/docs/request-handling#request-body</a> for detailed documentation. 064 * 065 * @author <a href="https://www.revetkn.com">Mark Allen</a> 066 */ 067@FunctionalInterface 068public interface RequestBodyMarshaler { 069 /** 070 * Given a request, the <em>Resource Method</em> that will handle it, and a {@link com.soklet.annotation.RequestBody}-annotated parameter + its type, convert the request body bytes into an instance of type {@code requestBodyType}. 071 * <p> 072 * This instance will be injected by Soklet when it invokes the <em>Resource Method</em> to handle the request. 073 * 074 * @param request the request whose body should be converted into a Java type 075 * @param resourceMethod the <em>Resource Method</em> that is configured to handle the request 076 * @param parameter the <em>Resource Method</em> parameter into which the returned instance will be injected 077 * @param requestBodyType the type of the <em>Resource Method</em> parameter (provided for convenience) 078 * @return the Java instance that corresponds to the request body bytes suitable for assignment to the <em>Resource Method</em> parameter, or {@link Optional#empty()} if no instance should be marshaled 079 */ 080 @Nonnull 081 Optional<Object> marshalRequestBody(@Nonnull Request request, 082 @Nonnull ResourceMethod resourceMethod, 083 @Nonnull Parameter parameter, 084 @Nonnull Type requestBodyType); 085}