c# - world - visual studio code asp net core




Wie lese ich den Anfragetext in einem asp.net-Kern-Webapi-Controller? (4)

Anscheinend können wir IHttpContextAccessor verwenden, um auf den http-Kontext in Controllern zuzugreifen. Müssen Sie nur in der Start-up-Klasse einspritzen und es in Ihre Controller bekommen.

  services.AddScoped<IHttpContextAccessor, HttpContextAccessor>();

Über diese können Sie auch im Konstruktor auf den Kontext zugreifen.

Ich versuche, den Anforderungshauptteil in der OnActionExecuting Methode zu lesen, OnActionExecuting jedoch immer den OnActionExecuting null für den Hauptteil.

var request = context.HttpContext.Request;
var stream = new StreamReader(request.Body);
var body = stream.ReadToEnd();

Ich habe versucht, die Stream-Position explizit auf 0 zu setzen, aber das hat auch nicht funktioniert. Da dies ASP.NET CORE ist, sind die Dinge meiner Meinung nach etwas anders. Ich kann hier alle Beispiele sehen, die auf alte Webapi-Versionen verweisen.
Gibt es eine andere Möglichkeit, dies zu tun?


Die IHttpContextAccessor Methode funktioniert, wenn Sie diese Route gehen möchten.

TLDR;

  • IHttpContextAccessor Sie den IHttpContextAccessor

  • Rewind - HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);

  • Read - System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body); JObject asObj = JObject.Parse(sr.ReadToEnd()); System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body); JObject asObj = JObject.Parse(sr.ReadToEnd());

More - Ein Versuch, ein übersichtliches, nicht kompiliertes Beispiel für die Elemente zu erstellen, die Sie benötigen, um einen verwendbaren IHttpContextAccessor . Die Antworten haben richtig darauf hingewiesen, dass Sie zum Anfang zurückkehren müssen, wenn Sie versuchen, den Anforderungshauptteil zu lesen. Die Eigenschaften " CanSeek , Position CanSeek Stream sind hilfreich, um dies zu überprüfen.

.NET Core DI-Dokumente

// First -- Make the accessor DI available
//
// Add an IHttpContextAccessor to your ConfigureServices method, found by default
// in your Startup.cs file:
// Extraneous junk removed for some brevity:
public void ConfigureServices(IServiceCollection services)
{
    // Typical items found in ConfigureServices:
    services.AddMvc(config => { config.Filters.Add(typeof(ExceptionFilterAttribute)); });
    // ...

    // Add or ensure that an IHttpContextAccessor is available within your Dependency Injection container
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

// Second -- Inject the accessor
//
// Elsewhere in the constructor of a class in which you want
// to access the incoming Http request, typically 
// in a controller class of yours:
public class MyResourceController : Controller
{
    public ILogger<PricesController> Logger { get; }
    public IHttpContextAccessor HttpContextAccessor { get; }

    public CommandController(
        ILogger<CommandController> logger,
        IHttpContextAccessor httpContextAccessor)
    {
        Logger = logger;
        HttpContextAccessor = httpContextAccessor;
    }

    // ...

    // Lastly -- a typical use 
    [Route("command/resource-a/{id}")]
    [HttpPut]
    public ObjectResult PutUpdate([FromRoute] string id, [FromBody] ModelObject requestModel)
    {
        if (HttpContextAccessor.HttpContext.Request.Body.CanSeek)
        {
            HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
            System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body);
            JObject asObj = JObject.Parse(sr.ReadToEnd());

            var keyVal = asObj.ContainsKey("key-a");
        }
    }
}    

Ich hatte ein ähnliches Problem bei der Verwendung von ASP.NET Core 2.1:

  • Ich benötige eine benutzerdefinierte Middleware, um die POST-Daten zu lesen und einige Sicherheitsüberprüfungen durchzuführen
  • Die Verwendung eines Berechtigungsfilters ist aufgrund der Vielzahl der betroffenen Aktionen nicht sinnvoll
  • Ich muss zulassen, dass Objekte in die Aktionen eingebunden werden ([FromBody] someObject). Vielen Dank an SaoBiz für den Hinweis auf diese Lösung.

Die naheliegende Lösung besteht also darin, dass die Anforderung zurückgespult werden kann. Stellen Sie jedoch sicher, dass die Bindung nach dem Lesen des Texts weiterhin funktioniert.

EnableRequestRewindMiddleware

public class EnableRequestRewindMiddleware
{
    private readonly RequestDelegate _next;

    ///<inheritdoc/>
    public EnableRequestRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public async Task Invoke(HttpContext context)
    {
        context.Request.EnableRewind();
        await _next(context);
    }
}

Startup.cs

(Platzieren Sie dies am Anfang der Configure-Methode.)

app.UseMiddleware<EnableRequestRewindMiddleware>();

Eine andere Middleware

Dies ist Teil der Middleware, die das Entpacken der POST-Informationen zum Überprüfen von Inhalten erfordert.

using (var stream = new MemoryStream())
{
    // make sure that body is read from the beginning
    context.Request.Body.Seek(0, SeekOrigin.Begin);
    context.Request.Body.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());

    // this is required, otherwise model binding will return null
    context.Request.Body.Seek(0, SeekOrigin.Begin);
}

In ASP.Net Core scheint es kompliziert zu sein, die Textanforderung mehrmals zu lesen. Wenn Ihr erster Versuch jedoch richtig ausgeführt wird, sollten Sie für die nächsten Versuche in Ordnung sein.

Ich habe mehrere Umkehrungen gelesen, indem ich beispielsweise den Körperstrom ersetzte, aber ich denke, dass das Folgende das sauberste ist:

Die wichtigsten Punkte sind

  1. um der Anfrage mitzuteilen, dass Sie ihren Text zweimal oder mehrmals lesen werden,
  2. den Körperstrom nicht zu schließen, und
  3. um es in seine Ausgangsposition zurückzuspulen, damit der interne Prozess nicht verloren geht.

[BEARBEITEN]

Wie Murad ausführt, können Sie auch die Erweiterung .Net Core 2.1 nutzen: EnableBuffering Speichert große Anforderungen auf der Festplatte, anstatt sie im Speicher zu EnableBuffering , und vermeidet so Probleme mit großen Streams im Speicher (Dateien, Bilder, ...). ). Sie können den temporären Ordner ändern, indem ASPNETCORE_TEMP Umgebungsvariable ASPNETCORE_TEMP Dateien werden gelöscht, sobald die Anforderung beendet ist.

In einem AuthorizationFilter können Sie Folgendes tun:

// Helper to enable request stream rewinds
using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableBodyRewind : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var bodyStr = "";
        var req = context.HttpContext.Request;

        // Allows using several time the stream in ASP.Net Core
        req.EnableRewind(); 

        // Arguments: Stream, Encoding, detect encoding, buffer size 
        // AND, the most important: keep stream opened
        using (StreamReader reader 
                  = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
        {
            bodyStr = reader.ReadToEnd();
        }

        // Rewind, so the core is not lost when it looks the body for the request
        req.Body.Position = 0;

        // Do whatever work with bodyStr here

    }
}



public class SomeController : Controller
{
    [HttpPost("MyRoute")]
    [EnableBodyRewind]
    public IActionResult SomeAction([FromBody]MyPostModel model )
    {
        // play the body string again
    }
}

Anschließend können Sie den Body wieder im Request-Handler verwenden.

Wenn Sie in Ihrem Fall ein Null-Ergebnis erhalten, bedeutet dies wahrscheinlich, dass der Körper bereits zu einem früheren Zeitpunkt gelesen wurde. In diesem Fall müssen Sie möglicherweise eine Middleware verwenden (siehe unten).

Seien Sie jedoch vorsichtig, wenn Sie mit großen Streams umgehen. Dieses Verhalten impliziert, dass alles in den Speicher geladen wird. Dies sollte nicht im Falle eines Datei-Uploads ausgelöst werden.

Möglicherweise möchten Sie dies als Middleware verwenden

Meins sieht so aus (auch wenn Sie große Dateien herunterladen / hochladen, sollte dies deaktiviert sein, um Speicherprobleme zu vermeiden):

public sealed class BodyRewindMiddleware
{
    private readonly RequestDelegate _next;

    public BodyRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try { context.Request.EnableRewind(); } catch { }
        await _next(context);
        // context.Request.Body.Dipose() might be added to release memory, not tested
    }
}
public static class BodyRewindExtensions
{
    public static IApplicationBuilder EnableRequestBodyRewind(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        return app.UseMiddleware<BodyRewindMiddleware>();
    }

}




asp.net-core