azure-maven-plugins
azure-maven-plugins copied to clipboard
Cannot use use byte[] when running in azure-functions-maven-plugin
Plugin name and version
azure-functions-maven-plugin 1.9.2
Plugin configuration in your pom.xml
App was created using archetype
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>${azure.functions.maven.plugin.version}</version>
<configuration>
<appName>${functionAppName}</appName>
<resourceGroup>java-functions-group</resourceGroup>
<appServicePlanName>java-functions-app-service-plan</appServicePlanName>
for all valid values -->
<region>westus</region>
<runtime>
<os>linux</os>
<javaVersion>11</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~3</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
package demo;
import java.io.ByteArrayOutputStream;
import java.util.Optional;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
public class Echo {
@FunctionName("Echo")
public HttpResponseMessage run(//
@HttpTrigger(name = "req", dataType = "binary", methods = {
HttpMethod.POST
}, authLevel = AuthorizationLevel.ANONYMOUS) //
final HttpRequestMessage<Optional<byte[]>> request, //
final ExecutionContext context) {
try {
final byte[] body = request.getBody().get();
try (
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
out.write(body);
out.flush();
return request.createResponseBuilder(HttpStatus.OK).body(out.toByteArray()).build();
}
} catch (final Throwable e) {
return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).body("Error").build();
}
}
}
Expected behavior
Works with HttpRequestMessage<Optional<byte[]>>, only works with HttpRequestMessage<Optional<String>>.
Actual behavior
[2021-03-15T19:50:00.233Z] Executed 'Functions.Echo' (Failed, Id=b78a95e8-06ff-4e4c-9299-b27065fb2bc7, Duration=133ms)
[2021-03-15T19:50:00.233Z] System.Private.CoreLib: Exception while executing function: Functions.Echo. System.Private.CoreLib: Result: Failure
[2021-03-15T19:50:00.233Z] Exception: ClassCastException: Cannot convert com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource@112b07b8to type com.microsoft.azure.functions.HttpRequestMessage<java.util.Optional<byte[]>>
[2021-03-15T19:50:00.234Z] Stack: java.lang.ClassCastException: Cannot convert com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource@112b07b8to type com.microsoft.azure.functions.HttpRequestMessage<java.util.Optional<byte[]>>
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.DataOperations.generalAssignment(DataOperations.java:191)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.DataOperations.apply(DataOperations.java:120)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.DataSource.computeByType(DataSource.java:56)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource.computeByType(RpcHttpRequestDataSource.java:20)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.DataSource.computeByName(DataSource.java:42)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource.computeByName(RpcHttpRequestDataSource.java:20)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.binding.BindingDataStore.getDataByName(BindingDataStore.java:55)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.broker.ParameterResolver.resolve(ParameterResolver.java:59)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.broker.ParameterResolver.resolve(ParameterResolver.java:42)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.broker.EnhancedJavaMethodExecutorImpl.execute(EnhancedJavaMethodExecutorImpl.java:53)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.broker.JavaFunctionBroker.invokeMethod(JavaFunctionBroker.java:57)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:33)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:10)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.handler.MessageHandler.handle(MessageHandler.java:45)
[2021-03-15T19:50:00.234Z] at com.microsoft.azure.functions.worker.JavaWorkerClient$StreamingMessagePeer.lambda$onNext$0(JavaWorkerClient.java:92)
[2021-03-15T19:50:00.234Z] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[2021-03-15T19:50:00.234Z] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[2021-03-15T19:50:00.234Z] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[2021-03-15T19:50:00.234Z] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[2021-03-15T19:50:00.234Z] at java.base/java.lang.Thread.run(Thread.java:834)
Steps to reproduce the problem
curl --request POST 'http://localhost:7071/api/Echo' --data 'hello'
@pauldaustin Thanks for your report. @amamounelsayed Could you please help check this issue?
One further comment. It seems that if I pass in the content-type application/octet-stream in the HTTP request it will work. But other values such as application/pdf throw the error.
In my view it shouldn't be a requirement to set the content type to application/octet-stream if you want to receive the content as a byte[].
I think the Azure Function deployment also has a similar issue.
I've actually move to directly calling the blob service rather than using @BlobInput or @BlobOutput as neither of those support the use of Input/Output Streams. Which would be required to process very large files.
@Flanker32 is there any update on this issue? I seem to be having the same problem with receiving application/pdf into a byte[] parameter.
@dkontyko Sorry for the trouble, I'm syncing with the function service team and will update here once we get any updates.
@dkontyko thanks for reaching out can you provided your function code and post request that to reproduce the issue. Thank you. As mentioned by @pauldaustin the work around is to use content-type application/octet-stream in your request header. It tells functions that data type of the coming request is bytes so can processing it correctly.
@kaibocai here's an example function with the POST request in the ps1 file. https://github.com/dkontyko/azure-function-bug-repro/tree/main
I tested that function locally in IntelliJ. It works if I change the Content-type to application/octet-stream, as you mentioned, but fails if it's application/pdf.
(Here's the actual repo that I was originally trying to use this on: https://github.com/dkontyko/RestPdfFormFiller, but I'm base64-encoding the file right now because I was having issues with whitespace characters getting mangled.)
Hi @dkontyko thanks very much for your updates and repo. I am trying to understand that when sending pdf data are we suppose to receive them as byte[] (sorry I am not quite familiar with front-end, would you mind to share some docs on this.). Currently, if content type is set to application/pdf, the azure functions will take the request data as string type, and later when using gson to convert string to byte[], gson will failed here and cause the issue. But if it's application/octet-stream, azure functions know the request data is byte[] type and will processing it correctly with it's own type transfer logic, that's why it works in this case. Thanks for your help.
@kaibocai, here's some references: https://www.iana.org/assignments/media-types/application/pdf and https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types.
PDF is a binary file type, so IMHO it should always be processed as a (binary) byte[], never a string. (I wonder if binary should be the case for all "application" types, but I don't know enough about HTTP to say that definitively. I think it would be more compliant with the RFC, though: https://datatracker.ietf.org/doc/html/rfc2046#section-4.5.3)
Thank you for looking into this.
Two issues need to be fixed here:
-
azure-function infrastructure needs to acknowledge/accept any mime types and not just e.g. application/octet-stream as binary content. suggestion: consider binary the default and only use string for a defined list of mime types like application/json etc. For http use-cases azure-functions is very limited if only few mime-types are supported (=configurable would be great)-> I guess we should open a separate issue for that, because it's not just related to java and maven.
-
The azure-function library needs to be modified in a way it supports any mime type and provided a general ByteArray Datasource or make this one work? https://github.com/Azure/azure-functions-java-worker/blob/dev/src/main/java/com/microsoft/azure/functions/worker/binding/RpcByteArrayDataSource.java