wake-up-neo.com

Zugriff auf Rohteilanforderung

Ich versuche, auf den rohen Eingabekörper/Stream einer Anfrage in ASP.net 5 zuzugreifen. In der Vergangenheit konnte ich die Position des Eingabestroms auf 0 zurücksetzen und in einen Speicherstrom einlesen, wenn ich dies jedoch versuche Aus dem Kontext ist der Eingabestrom entweder null oder es wird ein Fehler ausgegeben (System.NotSupportedException => "Die angegebene Methode wird nicht unterstützt.").

Im ersten Beispiel unten kann ich auf die unformatierte Anforderung in einem Controller zugreifen, wenn ich den Parameterobjekttyp der Controller-Methode als dynamisch deklariere. Aus verschiedenen Gründen ist dies keine Lösung, und ich muss auf den Rohanforderungshauptteil in einem Authentifizierungsfilter zugreifen.

Dieses Beispiel funktioniert, ist aber keine vernünftige Lösung:

[HttpPost("requestme")]
public string GetRequestBody([FromBody] dynamic body)
{   
    return body.ToString();
}

Wirft Fehler:

[HttpPost("requestme")]
public string GetRequestBody()
{
    var m = new MemoryStream();
    Request.Body.CopyTo(m);

    var contentLength = m.Length;

    var b = System.Text.Encoding.UTF8.GetString(m.ToArray());

    return b;
}

Wirft Fehler:

[HttpPost("requestme")]
public string GetRequestBody()
{
    Request.Body.Position = 0;
    var input = new StreamReader(Request.Body).ReadToEnd();

    return input;
}

Wirft Fehler:

[HttpPost("requestme")]
public string GetRequestBody()
{
    Request.Body.Position = 0;
    var input = new MemoryStream();
    Request.Body.CopyTo(input);

    var inputString = System.Text.Encoding.UTF8.GetString(input.ToArray());

    return inputString;
}

Ich muss auf den rohen Anfragetext jeder Anfrage zugreifen, die für eine von mir erstellte API eingeht.

Jede Hilfe oder Anleitung wäre sehr dankbar!

BEARBEITEN:

Hier ist der Code, in dem ich den Anfragetext lesen möchte.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Http;

namespace API.Filters
{
    public class CustomAuthorizationAttribute : Attribute, IAuthorizationFilter
    {
        public CustomAuthorizationAttribute()
        { }

        public void OnAuthorization(AuthorizationContext context)
        {
            if (context == null)
                throw new ArgumentNullException("OnAuthorization AuthorizationContext context can not be null.");
            else
            {
                if (this.AuthorizeCore(context.HttpContext) == false)
                {
                    // Do Other Stuff To Check Auth
                }
                else
                {
                    context.Result = new HttpUnauthorizedResult();
                }
            }
        }

        protected virtual bool AuthorizeCore(HttpContext httpContext)
        {
            var result = false;

            using (System.IO.MemoryStream m = new System.IO.MemoryStream())
            {
                try
                {
                    if (httpContext.Request.Body.CanSeek == true)
                        httpContext.Request.Body.Position = 0;

                    httpContext.Request.Body.CopyTo(m);

                    var bodyString = System.Text.Encoding.UTF8.GetString(m.ToArray());

                    return CheckBody(bodyString); // Initial Auth Check returns true/false <-- Not Shown In Code Here on Stack Overflow
                }
                catch (Exception ex)
                {
                    Logger.WriteLine(ex.Message);
                }
            }
                return false;
        }
    }
}

Auf diesen Code kann zugegriffen werden, wenn eine Controller-Methode aufgerufen wird, die mit dem CustomAuthorization-Attribut markiert ist.

[Filters.CustomAuthorizationAuthorization]
[HttpPost]
public ActionResult Post([FromBody]UserModel Profile)
{
    // Process Profile
}
9
Badger Dev

Die Implementierung von Request.Body hängt von der Controller-Aktion ab. 

Wenn die Aktion Parameter enthält, wird sie von Microsoft.AspNet.WebUtilities.FileBufferingReadStream implementiert, was die Suche (Request.Body.CanSeek == true) unterstützt. Dieser Typ unterstützt auch das Einstellen des Request.Body.Position.

Wenn Ihre Aktion jedoch keine Parameter enthält, wird sie von Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody implementiert, wodurch nicht support (Request.Body.CanSeek == false) unterstützt wird. Das bedeutet, dass Sie die Position -Eigenschaft nicht anpassen können, und Sie können einfach den Stream lesen.

Dieser Unterschied hat wahrscheinlich mit der Tatsache zu tun, dass MVC die Parameterwerte aus dem Anforderungstext extrahieren muss. Daher muss die Anforderung gelesen werden.

In Ihrem Fall hat Ihre Aktion keine Parameter. Daher wird Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody verwendet, der eine Ausnahme auslöst, wenn Sie versuchen, die Position-Eigenschaft festzulegen.


Lösung : entweder nicht die Position setzen oder prüfen, ob Sie tatsächlich können zuerst die Position setzen:

if (Request.Body.CanSeek)
{
    // Reset the position to zero to read from the beginning.
    Request.Body.Position = 0;
}

var input = new StreamReader(Request.Body).ReadToEnd();
8
Henk Mollema

Die Ausnahmen, die Sie in Ihren drei letzten Ausschnitten sehen, sind die direkte Folge des mehrmaligen Lesens des Anfragetextes - einmal von MVC 6 und einmal in Ihrem benutzerdefinierten Code -, wenn Sie einen gestreamten Host wie IIS oder WebListener verwenden. Sie finden diese SO -Frage für weitere Informationen: Lesen Sie den Body zweimal in Asp.Net 5 .

Ich erwarte jedoch nur, dass dies bei der Verwendung von application/x-www-form-urlencoded der Fall ist, da MVC nicht in der Lage wäre, den Anforderungsdatenstrom mit längeren Anforderungen wie dem Hochladen von Dateien zu lesen. Wenn dies nicht der Fall ist, ist es wahrscheinlich ein MVC-Fehler, den Sie unter https://github.com/aspnet/Mvc melden sollten.

Für Problemumgehungen sollten Sie sich diese SO Antwort ansehen, in der erklärt wird, wie Sie context.Request.ReadFormAsync verwenden oder manuelle Pufferung hinzufügen können: Lesen Sie den Hauptteil in Asp.Net 5 zweimal.

app.Use(next => async context => {
    // Keep the original stream in a separate
    // variable to restore it later if necessary.
    var stream = context.Request.Body;

    // Optimization: don't buffer the request if
    // there was no stream or if it is rewindable.
    if (stream == Stream.Null || stream.CanSeek) {
        await next(context);

        return;
    }

    try {
        using (var buffer = new MemoryStream()) {
            // Copy the request stream to the memory stream.
            await stream.CopyToAsync(buffer);

            // Rewind the memory stream.
            buffer.Position = 0L;

            // Replace the request stream by the memory stream.
            context.Request.Body = buffer;

            // Invoke the rest of the pipeline.
            await next(context);
        }
    }

    finally {
        // Restore the original stream.
        context.Request.Body = stream;
    }
});
6
Pinpoint

Ich hatte gerade das gleiche Problem. Entfernen Sie die Parameter aus der Methodensignatur und lesen Sie den Request.Body-Stream wie gewünscht.

2
joe

Sie müssen Request.EnableRewind () aufrufen, damit der Stream zurückgespult werden kann, damit Sie ihn lesen können.

string bodyAsString;
Request.EnableRewind();
using (var streamReader = new StreamReader(Request.Body, Encoding.UTF8))
{
    bodyAsString = streamReader.ReadToEnd();
}
0
Steve Peirce