Я пытаюсь установить тип содержимого запроса multipart/form-data
GET, который пропускает границу. Я пробовал этот код:
public class BoundaryMiddleware
{
private readonly RequestDelegate _next;
public BoundaryMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.Value.Contains("api/webhook"))
{
if (context.Request.ContentType == "multipart/form-data")
{
context.Request.ContentType = $"multipart/form-data; boundary=\"{Guid.NewGuid()}\"";
}
}
await _next.Invoke(context);
}
}
И файл запуска:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory,
IConfiguration configuration)
{
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
loggerFactory.AddDebug();
}
app.UseMiddleware<BoundaryMiddleware>();
app.UseMvc();
}
}
Однако этот код вызывает следующее исключение:
System.IO.IOException: Unexpected end of Stream, the content may have already been read by another component.
at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.MultipartReader.ReadNextSectionAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.Features.FormFeature.InnerReadFormAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.ModelBinding.FormValueProviderFactory.AddValueProviderAsync(ValueProviderFactoryContext context)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.CreateAsync(ActionContext actionContext, IList`1 factories)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.CreateAsync(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
Есть ли способ изменить это? Спасибо
Обновлено: это запрос, переданный в веб-приложение:
GET /api/webhook HTTP/1.1
Host: localhost:5000
content-type: multipart/form-data
cache-control: no-cache
Редактировать 2: если я отправлю запрос как таковой, возникнет это исключение:
System.IO.InvalidDataException: Missing content-type boundary.
at Microsoft.AspNetCore.Http.Features.FormFeature.GetBoundary(MediaTypeHeaderValue contentType, Int32 lengthLimit)
at Microsoft.AspNetCore.Http.Features.FormFeature.InnerReadFormAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.ModelBinding.FormValueProviderFactory.AddValueProviderAsync(ValueProviderFactoryContext context)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.CreateAsync(ActionContext actionContext, IList`1 factories)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.CreateAsync(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
зачем вам вводить этот идентификатор? Я имею в виду, что вы можете сделать это в своем действии контроллера, пусть маршрутизация сделает свое дело.
Вы не можете просто добавить случайный идентификатор границы. Этот идентификатор должен использоваться во всем теле запроса, чтобы это работало. – Почему бы вам вместо этого не попытаться исправить ваш клиент, если он выдает неверный запрос?
Я не являюсь владельцем кода, который вызывает мой API, и запрос является запросом на получение, поэтому тело ничего не содержит.
содержимое multipart/form-data как получить? Я что-то пропустил?
Я знаю, что это странно, но звонящий реализовал свой API таким образом.
Но вопрос в том, почему вы даже пытаетесь добавить составную границу в заголовок вашего типа контента, они не отправляют вам что-то, верно?
Да, но без границы генерируется другое исключение: System.IO.InvalidDataException: Missing content-type boundary.
Вместо этого вы можете попробовать удалить заголовок. Почти уверен, что это бессмысленно для запроса GET
Для HTTP GET HTTP-заголовок Content-Type не должен иметь никакого значения, HTTP-заголовок Content-Type имеет значение требуется для установки в запросах с телом (обычно PUT и POST).
На самом деле получить можно с телом, смотрите здесь, если ваш API должен поддерживать это, удачи, но это не невозможно!
В противном случае, если ваши потребители API не отправляют тело, просто сделайте следующее:
[AllowAnonymous]
[HttpGet]
[Route("api/webhook")]
public IActionResult GetWebhook()
{
// rock and roll
return Ok();
}
С тестом CURL (используйте местный)
curl -X GET "https://localhost:44395/api/webhook" -H "content-type: multipart/form-data" --verbose
Нет необходимости в этом перехватчике запросов.
Заголовки .Net HTTP, как правило, немного забавны, и фреймворк будет делать различные предположения при попытке преобразовать необработанный http-запрос в HttpRequest
, а затем происходит дальнейшая обработка при вызове действия на контроллере.
Учитывая, что тела нет, нет смысла иметь заголовок Content-Type или граничное значение. Возможно, вам будет эффективнее вообще удалить заголовок.
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.Value.Contains("api/webhook"))
{
if (context.Request.ContentType == "multipart/form-data")
context.Request.Headers.Remove("Content-Type");
}
await _next.Invoke(context);
}
На ваш вопрос сложно ответить без звонка в сервис, по ошибке похоже, что поток уже прочитан, нужно установить поток в начало перед попыткой его прочитать.