mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(Webhook Node): Binary data handling (#7804)
Github issue / Community forum post (link here to close automatically):
This commit is contained in:
parent
92fa6233d3
commit
565b409a82
|
@ -304,14 +304,20 @@ export async function executeWebhook(
|
||||||
additionalData.httpRequest = req;
|
additionalData.httpRequest = req;
|
||||||
additionalData.httpResponse = res;
|
additionalData.httpResponse = res;
|
||||||
|
|
||||||
const binaryData = workflow.expression.getSimpleParameterValue(
|
let binaryData;
|
||||||
workflowStartNode,
|
|
||||||
'={{$parameter["options"]["binaryData"]}}',
|
const nodeVersion = workflowStartNode.typeVersion;
|
||||||
executionMode,
|
if (nodeVersion === 1) {
|
||||||
additionalKeys,
|
// binaryData option is removed in versions higher than 1
|
||||||
undefined,
|
binaryData = workflow.expression.getSimpleParameterValue(
|
||||||
false,
|
workflowStartNode,
|
||||||
);
|
'={{$parameter["options"]["binaryData"]}}',
|
||||||
|
executionMode,
|
||||||
|
additionalKeys,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let didSendResponse = false;
|
let didSendResponse = false;
|
||||||
let runExecutionDataMerge = {};
|
let runExecutionDataMerge = {};
|
||||||
|
@ -321,6 +327,7 @@ export async function executeWebhook(
|
||||||
let webhookResultData: IWebhookResponseData;
|
let webhookResultData: IWebhookResponseData;
|
||||||
|
|
||||||
// if `Webhook` or `Wait` node, and binaryData is enabled, skip pre-parse the request-body
|
// if `Webhook` or `Wait` node, and binaryData is enabled, skip pre-parse the request-body
|
||||||
|
// always falsy for versions higher than 1
|
||||||
if (!binaryData) {
|
if (!binaryData) {
|
||||||
const { contentType, encoding } = req;
|
const { contentType, encoding } = req;
|
||||||
if (contentType === 'multipart/form-data') {
|
if (contentType === 'multipart/form-data') {
|
||||||
|
@ -337,7 +344,19 @@ export async function executeWebhook(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await parseBody(req);
|
if (nodeVersion > 1) {
|
||||||
|
if (
|
||||||
|
contentType?.startsWith('application/json') ||
|
||||||
|
contentType?.startsWith('text/plain') ||
|
||||||
|
contentType?.startsWith('application/x-www-form-urlencoded') ||
|
||||||
|
contentType?.endsWith('/xml') ||
|
||||||
|
contentType?.endsWith('+xml')
|
||||||
|
) {
|
||||||
|
await parseBody(req);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await parseBody(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class Webhook extends Node {
|
||||||
icon: 'file:webhook.svg',
|
icon: 'file:webhook.svg',
|
||||||
name: 'webhook',
|
name: 'webhook',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
description: 'Starts the workflow when a webhook is called',
|
description: 'Starts the workflow when a webhook is called',
|
||||||
eventTriggerDescription: 'Waiting for you to call the Test URL',
|
eventTriggerDescription: 'Waiting for you to call the Test URL',
|
||||||
activationMessage: 'You can now make calls to your production webhook URL.',
|
activationMessage: 'You can now make calls to your production webhook URL.',
|
||||||
|
@ -125,6 +125,17 @@ export class Webhook extends Node {
|
||||||
return this.handleFormData(context);
|
return this.handleFormData(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nodeVersion = context.getNode().typeVersion;
|
||||||
|
if (nodeVersion > 1 && !req.body && !options.rawBody) {
|
||||||
|
try {
|
||||||
|
return await this.handleBinaryData(context);
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.rawBody && !req.rawBody) {
|
||||||
|
await req.readRawBody();
|
||||||
|
}
|
||||||
|
|
||||||
const response: INodeExecutionData = {
|
const response: INodeExecutionData = {
|
||||||
json: {
|
json: {
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
|
@ -135,7 +146,7 @@ export class Webhook extends Node {
|
||||||
binary: options.rawBody
|
binary: options.rawBody
|
||||||
? {
|
? {
|
||||||
data: {
|
data: {
|
||||||
data: req.rawBody.toString(BINARY_ENCODING),
|
data: (req.rawBody ?? '').toString(BINARY_ENCODING),
|
||||||
mimeType: req.contentType ?? 'application/json',
|
mimeType: req.contentType ?? 'application/json',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -215,6 +226,7 @@ export class Webhook extends Node {
|
||||||
};
|
};
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (const key of Object.keys(files)) {
|
for (const key of Object.keys(files)) {
|
||||||
const processFiles: MultiPartFormData.File[] = [];
|
const processFiles: MultiPartFormData.File[] = [];
|
||||||
let multiFile = false;
|
let multiFile = false;
|
||||||
|
@ -247,6 +259,7 @@ export class Webhook extends Node {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { workflowData: [[returnItem]] };
|
return { workflowData: [[returnItem]] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,12 +285,18 @@ export class Webhook extends Node {
|
||||||
|
|
||||||
const binaryPropertyName = (options.binaryPropertyName || 'data') as string;
|
const binaryPropertyName = (options.binaryPropertyName || 'data') as string;
|
||||||
const fileName = req.contentDisposition?.filename ?? uuid();
|
const fileName = req.contentDisposition?.filename ?? uuid();
|
||||||
returnItem.binary![binaryPropertyName] = await context.nodeHelpers.copyBinaryFile(
|
const binaryData = await context.nodeHelpers.copyBinaryFile(
|
||||||
binaryFile.path,
|
binaryFile.path,
|
||||||
fileName,
|
fileName,
|
||||||
req.contentType ?? 'application/octet-stream',
|
req.contentType ?? 'application/octet-stream',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!binaryData.data) {
|
||||||
|
return { workflowData: [[returnItem]] };
|
||||||
|
}
|
||||||
|
|
||||||
|
returnItem.binary![binaryPropertyName] = binaryData;
|
||||||
|
|
||||||
return { workflowData: [[returnItem]] };
|
return { workflowData: [[returnItem]] };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new NodeOperationError(context.getNode(), error as Error);
|
throw new NodeOperationError(context.getNode(), error as Error);
|
||||||
|
|
|
@ -202,6 +202,7 @@ export const optionsProperty: INodeProperties = {
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/httpMethod': ['PATCH', 'PUT', 'POST'],
|
'/httpMethod': ['PATCH', 'PUT', 'POST'],
|
||||||
|
'@version': [1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -212,15 +213,28 @@ export const optionsProperty: INodeProperties = {
|
||||||
name: 'binaryPropertyName',
|
name: 'binaryPropertyName',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'data',
|
default: 'data',
|
||||||
required: true,
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
binaryData: [true],
|
binaryData: [true],
|
||||||
|
'@version': [1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Name of the binary property to write the data of the received file to. If the data gets received via "Form-Data Multipart" it will be the prefix and a number starting with 0 will be attached to it.',
|
'Name of the binary property to write the data of the received file to. If the data gets received via "Form-Data Multipart" it will be the prefix and a number starting with 0 will be attached to it.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
name: 'binaryPropertyName',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
'@version': [1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'Name of the binary property to write the data of the received file to, only relevant if binary data is received',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Ignore Bots',
|
displayName: 'Ignore Bots',
|
||||||
name: 'ignoreBots',
|
name: 'ignoreBots',
|
||||||
|
@ -248,6 +262,9 @@ export const optionsProperty: INodeProperties = {
|
||||||
name: 'rawBody',
|
name: 'rawBody',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'@version': [1],
|
||||||
|
},
|
||||||
hide: {
|
hide: {
|
||||||
binaryData: [true],
|
binaryData: [true],
|
||||||
noResponseBody: [true],
|
noResponseBody: [true],
|
||||||
|
@ -257,6 +274,19 @@ export const optionsProperty: INodeProperties = {
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
|
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
|
||||||
description: 'Raw body (binary)',
|
description: 'Raw body (binary)',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Raw Body',
|
||||||
|
name: 'rawBody',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
noResponseBody: [true],
|
||||||
|
'@version': [1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'Whether to return the raw body',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Response Data',
|
displayName: 'Response Data',
|
||||||
name: 'responseData',
|
name: 'responseData',
|
||||||
|
|
Loading…
Reference in a new issue