Spring Webflux Functional Endpoint – File Upload

In this blog using the Spring WebFlux module, we are going to leverage the functional programming style to define web endpoints using functional endpoints and routing functions.

  1. Functional Endpoints: Spring WebFlux allows to define web endpoints in a functional way using functional programming constructs. Instead of annotating classes and methods as in Spring MVC, we can define routing and handling of requests using functional interfaces and lambda expressions.
  2. Router Functions: Router functions are used to define routes and mappings for incoming requests. These functions are responsible for routing incoming requests to the appropriate handler functions based on criteria such as URL paths, HTTP methods, etc

Here’s an example demonstrating the usage of functional endpoints in Spring WebFlux:

@Controller
public class UploadController {

	@Autowired
	UploadService service;

	@Bean
	@RouterOperations({

		@RouterOperation(path = "/upload", produces = {
				MediaType.APPLICATION_JSON_VALUE }, consumes = "multipart/form-data", method = RequestMethod.POST, operation = @Operation(operationId = "Upload", responses = {
						@ApiResponse(responseCode = Constants.responseCodes.SUCCESS, description = "File Upload Success", content = @Content(schema = @Schema(implementation = Employee.class))),
						@ApiResponse(responseCode = Constants.responseCodes.INTERNAL_SERVER_ERROR, description = "File Upload Failed") }, requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, schemaProperties = {
								@SchemaProperty(name = "file", schema = @Schema(type = "string", format = "binary", contentMediaType = MediaType.MULTIPART_FORM_DATA_VALUE)) }))

						)) })

	RouterFunction<ServerResponse> routeUpload() {

		return RouterFunctions.route(
				RequestPredicates.POST("/upload").and(RequestPredicates.contentType(MediaType.MULTIPART_FORM_DATA)),
				req -> req.body(BodyExtractors.toMultipartData()).flatMap(file -> service.upload(file).collectList())
				.flatMap(data -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(data)));
	}
}

In this example, the functional endpoint “/upload” is defined using RouterFunction. The @RouterOperations and @RouterOperation annotations are used to display REST API’s on the swagger-ui. Additional properties like API responses, request body schemas etc are documented using the @Operation annotation. 

The defined route uploads the file contents to DB when accessed via a POST request to “/upload”.

@Service
@Slf4j
public class UploadService {
	
	@Autowired
	UploadRepository repo;

	public Flux<Employee> upload(MultiValueMap<String, Part> file){

		log.info("file upload started...");
		Map<String, Part> part = file.toSingleValueMap();
		FilePart filePart = (FilePart) part.get("file");		
		return DataBufferUtils.join(filePart.content()).map(data ->extracted(data)).
				map(this::processFile).
				flatMapMany(row -> {
			List<Employee> employees = new ArrayList<>();
			for(int i=0;i<row.size();i++) {
				String[] nextLine = row.get(i).split(",");
				Employee emp = Employee.builder().name(nextLine[0]).department(nextLine[1]).build();
				employees.add(emp);				
			}
			return repo.saveAll(employees);
		});
	}

	private String extracted(DataBuffer data) {
		byte[] bytes = data.asByteBuffer().array();
		data.read(bytes);
		DataBufferUtils.release(data);
		return new String(bytes,StandardCharsets.UTF_8);
	}
	
	private List<String> processFile(String line){
		Supplier<Stream<String>> lines = line::lines;
		return lines.get().collect(Collectors.toList());
		
	}
}

Couchbase DB configuration are defined in application.properties:

spring.couchbase.connection-string=localhost
spring.couchbase.password=${PASSWORD}
spring.couchbase.username=${USERNAME}
spring.data.couchbase.auto-index=true
spring.data.couchbase.bucket-name=employees

We can test the API using Swagger which will be available at http://server:port/context-path/swagger-ui.html

Response:

For complete source code: https://github.com/MMahendravarman/Springboot_Examples

Leave a Reply

Your email address will not be published. Required fields are marked *