using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Extensions;
using Elsa.Results;
using Elsa.Services;
using Elsa.Services.Models;
using Microsoft.AspNetCore.Http;
namespace Elsa.Activities.Http.Activities
{
[ActivityDefinition(
Category = "HTTP",
DisplayName = "Write HTTP Response",
Description = "Write an HTTP response.",
RuntimeDescription = "x => !!x.state.statusCode ? `Send an HTTP ${ x.state.statusCode } - ${ x.state.contentType } response` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class WriteHttpResponse : Activity
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
private readonly IHttpContextAccessor httpContextAccessor;
public static IEnumerable GetStatusCodes()
{
return new[]
{
new SelectItem
{
Label = "2xx",
Options = new object[] { 200, 201, 202, 203, 204 }
},
new SelectItem
{
Label = "3xx",
Options = new object[] { 301, 302, 304, 307, 308 }
},
new SelectItem
{
Label = "4xx",
Options = new object[] { 400, 401, 402, 403, 404, 405, 409, 410, 412, 413, 415, 417, 418, 420, 428, 429 }
}
};
}
public WriteHttpResponse(
IWorkflowExpressionEvaluator expressionEvaluator,
IHttpContextAccessor httpContextAccessor)
{
this.expressionEvaluator = expressionEvaluator;
this.httpContextAccessor = httpContextAccessor;
}
///
/// The HTTP status code to return.
///
[ActivityProperty(
Type = ActivityPropertyTypes.Select,
Hint = "The HTTP status code to write."
)]
[SelectOptions(nameof(GetStatusCodes), typeof(WriteHttpResponse))]
public HttpStatusCode StatusCode
{
get => GetState(() => HttpStatusCode.OK);
set => SetState(value);
}
///
/// The content to send along with the response
///
[ActivityProperty(Hint = "The HTTP content to write.")]
[ExpressionOptions(Multiline = true)]
public WorkflowExpression Content
{
get => GetState(() => new WorkflowExpression(LiteralEvaluator.SyntaxName, ""));
set => SetState(value);
}
///
/// The Content-Type header to send along with the response.
///
[ActivityProperty(
Type = ActivityPropertyTypes.Select,
Hint = "The HTTP content type header to write."
)]
[SelectOptions("text/plain", "text/html", "application/json", "application/xml")]
public string ContentType
{
get => GetState();
set => SetState(value);
}
///
/// The headers to send along with the response. One 'header: value' pair per line.
///
[ActivityProperty(Hint = "The headers to send along with the response. One 'header: value' pair per line.")]
public WorkflowExpression ResponseHeaders
{
get => GetState(() => new WorkflowExpression(LiteralEvaluator.SyntaxName, ""));
set => SetState(value);
}
protected override async Task OnExecuteAsync(
WorkflowExecutionContext workflowContext,
CancellationToken cancellationToken)
{
var response = httpContextAccessor.HttpContext.Response;
if (response.HasStarted)
return Fault("Response has already started");
response.StatusCode = (int)StatusCode;
response.ContentType = ContentType;
var headersText = await expressionEvaluator.EvaluateAsync(
ResponseHeaders,
workflowContext,
cancellationToken
);
if (headersText != null)
{
var headersQuery =
from line in Regex.Split(headersText, "\\n", RegexOptions.Multiline)
let pair = line.Split(':', '=')
select new KeyValuePair(pair[0], pair[1]);
foreach (var header in headersQuery)
{
var headerValueExpression = new WorkflowExpression(ResponseHeaders.Syntax, header.Value);
response.Headers[header.Key] = await expressionEvaluator.EvaluateAsync(
headerValueExpression,
workflowContext,
cancellationToken
);
}
}
var bodyText = await expressionEvaluator.EvaluateAsync(Content, workflowContext, cancellationToken);
if (!string.IsNullOrWhiteSpace(bodyText))
{
await response.WriteAsync(bodyText, cancellationToken);
}
return Done();
}
}
}