Я пытаюсь создать веб-перехватчик для получения сообщений с номера телефона Twilio. Но вместо простого веб-перехватчика, который изменит данные и немедленно вернет результат в Twilio, мне нужен этот веб-перехватчик, чтобы передать сообщение Twilio во внутренний API, дождаться ответа и потом вернуть результат в Twilio.
Вот некоторый общий код, который я придумал, и я надеялся, что он будет работать.
public async Task<HttpResponseMessage> ReceiveAndForwardSms(HttpContent smsContent)
{
var client = new HttpClient();
HttpResponseMessage response = await client.PostAsync(Environment.GetEnvironmentVariable("requestUriBase") + "/api/SmsHandler/PostSms", smsContent);
return response;
}
Проблема с этим кодом в том, что Twilio сразу возвращает код ошибки 415 (Unsupported Media Type
) перед входом в функцию.
Когда я пытаюсь принять «правильный тип» (Twilio.AspNet.Common.SmsRequest
), я не могу вставить SmsRequest
обратно в объект, закодированный формой, и отправить его через client.PostAsync()
...
Бывший.:
public async Task<HttpResponseMessage> ReceiveAndForwardSms([FromForm]SmsRequest smsRequest)
{
var client = new HttpClient();
var stringContent = new StringContent(smsRequest.ToString());
HttpResponseMessage response = await client.PostAsync(Environment.GetEnvironmentVariable("requestUriBase") + "/api/SmsHandler/PostSms", stringContent);
return response;
}
TLDR Ваши варианты:
HttpRequestMessage
и отправьте его в свою частную службу, затем сопоставьте ответ вашей частной службы с ответом обратно в Twilio.Похоже, вы пытаетесь построить обратный прокси. Очень часто перед вашим веб-приложением помещают обратный прокси-сервер для терминации SSL, кэширования, маршрутизации на основе имени хоста или URL-адреса и т. д. Обратный прокси-сервер получит HTTP-запрос Twilio, а затем перенаправит его в нужную частную службу. Частный сервис отвечает, и обратный прокси-сервер перенаправляет его обратно в Twilio.
Я бы порекомендовал использовать существующий обратный прокси вместо того, чтобы создавать эту функциональность самостоятельно. Если вы действительно хотите создать его самостоятельно, вот пример, который мне удалось заставить работать:
В вашем обратном прокси-проекте добавьте контроллер как таковой:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
namespace ReverseProxy.Controllers;
public class SmsController : Controller
{
private static readonly HttpClient HttpClient;
private readonly ILogger<SmsController> logger;
private readonly string twilioWebhookServiceUrl;
static SmsController()
{
// don't do this in production!
var insecureHttpClientHandler = new HttpClientHandler();
insecureHttpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true;
HttpClient = new HttpClient(insecureHttpClientHandler);
}
public SmsController(ILogger<SmsController> logger, IConfiguration configuration)
{
this.logger = logger;
twilioWebhookServiceUrl = configuration["TwilioWebhookServiceUrl"];
}
public async Task Index()
{
using var serviceRequest = new HttpRequestMessage(HttpMethod.Post, twilioWebhookServiceUrl);
foreach (var header in Request.Headers)
{
serviceRequest.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
serviceRequest.Content = new FormUrlEncodedContent(
Request.Form.ToDictionary(
kv => kv.Key,
kv => kv.Value.ToString()
)
);
var serviceResponse = await HttpClient.SendAsync(serviceRequest);
Response.ContentType = "application/xml";
var headersDenyList = new HashSet<string>()
{
"Content-Length",
"Date",
"Transfer-Encoding"
};
foreach (var header in serviceResponse.Headers)
{
if (headersDenyList.Contains(header.Key)) continue;
logger.LogInformation("Header: {Header}, Value: {Value}", header.Key, string.Join(',', header.Value));
Response.Headers.Add(header.Key, new StringValues(header.Value.ToArray()));
}
await serviceResponse.Content.CopyToAsync(Response.Body);
}
}
Это примет запрос веб-перехватчика Twilio и перенаправит все заголовки и содержимое в частную веб-службу. Имейте в виду, хотя мне удалось взломать это, пока оно не заработает, оно, вероятно, небезопасно и неэффективно. Вам, вероятно, придется сделать гораздо больше, чтобы это стало кодом производственного уровня. Используйте на свой риск.
В проекте ASP.NET Core для вашей частной службы используйте TwilioController
, чтобы принять запрос:
using Microsoft.AspNetCore.Mvc;
using Twilio.AspNet.Common;
using Twilio.AspNet.Core;
using Twilio.TwiML;
namespace Service.Controllers;
public class SmsController : TwilioController
{
private readonly ILogger<SmsController> logger;
public SmsController(ILogger<SmsController> logger)
{
this.logger = logger;
}
public IActionResult Index(SmsRequest smsRequest)
{
logger.LogInformation("SMS Received: {SmsId}", smsRequest.SmsSid);
var response = new MessagingResponse();
response.Message($"You sent: {smsRequest.Body}");
return TwiML(response);
}
}
Вместо того, чтобы проксировать запрос с использованием хрупкого кода в обратном прокси-контроллере, я бы рекомендовал установить YARP в ваш проект обратного прокси, который представляет собой библиотеку обратного прокси на основе ASP.NET Core.
dotnet add package Yarp.ReverseProxy
Затем добавьте следующую конфигурацию в appsettings.json:
{
...
"ReverseProxy": {
"Routes": {
"SmsRoute" : {
"ClusterId": "SmsCluster",
"Match": {
"Path": "/sms"
}
}
},
"Clusters": {
"SmsCluster": {
"Destinations": {
"SmsService1": {
"Address": "https://localhost:7196"
}
}
}
}
}
}
Эта конфигурация будет перенаправлять любой запрос по пути /Смс в вашу частную службу ASP.NET Core, которая на моем локальном компьютере работает по адресу https://локальный: 7196.
Вам также необходимо обновить файл Программа.cs, чтобы начать использовать YARP:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
Когда вы сейчас запустите оба проекта, запрос веб-перехватчика Twilio к /смс будет переадресован вашей частной службе, ваша частная служба ответит, а служба обратного прокси-сервера перешлет ответ обратно в Twilio.
Используя YARP, вы можете сделать гораздо больше с помощью конфигурации или даже программно, поэтому, если вам интересно, я бы проверил файл Ярп документы.
Если у вас уже есть обратный прокси-сервер, такой как NGINX, HAProxy, F5 и т. д., может быть проще настроить его для пересылки вашего запроса вместо использования YARP.
PS: вот исходный код решения для взлома и YARP
Круто, спасибо за подробный ответ (и предупреждения... Я бы сделал это в продакшене, если бы не было сказано не LOL). IDK, почему я думал, что будет существовать универсальное, короткое и простое решение для переадресации, которое будет легко реализовать.
Я надеюсь, что это понятно, если не ЛМК, и я сделаю все возможное, чтобы прояснить.