wake-up-neo.com

Wie kann ich bei Verwendung von IHttpActionResult benutzerdefinierte Kopfzeilen festlegen?

In ASP.NET-Web-API 2 bietet IHttpActionResult einen großen Wert bei der Vereinfachung von Controller-Code. Ich möchte nur ungern aufhören, aber ich habe ein Problem. 

Ich muss ein ETag für eine ausgehende Antwort festlegen, und ich kann keine Eigenschaft finden, die mir Zugriff auf die Header der Antwort gibt. Im Moment verwende ich die Helper-Methode Ok<T>(T content) von ApiController, die ein OkNegotiatedContentResult<T> -Objekt zurückgibt. Das scheint nichts offengelegt zu haben, was es mir erlaubt, die Header zu ändern.

Fehlt mir etwas oder gibt es wirklich keine Möglichkeit, dies zu tun, während Sie die IHttpActionResulttypen von stock verwenden? Ich habe einen Message-Handler in Betracht gezogen, aber dann müsste ich herausfinden, wie man den ETag aus der Aktion herausgibt (die ETags werden für verschiedene Aktionen unterschiedlich generiert, es ist also keine Frage des Generic-Handlers für alle Aktionen).

Ich möchte vermeiden, die rohe HttpResponseMessage verwenden zu müssen, aber im Moment sieht das schwierig aus.

38
ehdv

Für Ihr Szenario müssen Sie eine benutzerdefinierte IHttpActionResult erstellen. Folgendes ist ein Beispiel, bei dem ich von OkNegotiatedContentResult<T> ableite, während es Content-Negotiation ausgeführt wird und den Ok-Statuscode einstellt.

public class CustomOkResult<T> : OkNegotiatedContentResult<T>
{
    public CustomOkResult(T content, ApiController controller)
        : base(content, controller) { }

    public CustomOkResult(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters) 
        : base(content, contentNegotiator, request, formatters) { }

    public string ETagValue { get; set; }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.ExecuteAsync(cancellationToken);

        response.Headers.ETag = new EntityTagHeaderValue(this.ETagValue);

        return response;
    }        
}

Controller

public class ValuesController : ApiController
{
    public IHttpActionResult Get()
    {
        return new CustomOkResult<string>(content: "Hello World!", controller: this)
            {
                    ETagValue = "You ETag value"
            };
    }
}

Beachten Sie, dass Sie auch von NegotiatedContentResult<T> ableiten können. In diesem Fall müssen Sie den StatusCode selbst angeben. Hoffe das hilft.

Sie finden den Quellcode von OkNegotiatedContentResult<T> und NegotiatedContentResult<T>, die, wie Sie sich vorstellen können, eigentlich einfach sind.

33
Kiran Challa

Sie können eine HttpResponseMessage erstellen, bei Bedarf Header hinzufügen und anschließend ResponseMessageResult daraus erstellen:

HttpResponseMessage response =new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("MyHeader", "MyHeaderValue");
return ResponseMessage(response);
31
AlexACD

Hier ist meine einfache Implementierung ohne ActionFilterAttributes und ähnelt der Antwort von AlexACD. Meine Lösung verwendet das ResponseMessageResult, das die IHttpActionResult-Schnittstelle implementiert.

HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
responseMessage.Headers.Add("Headername", "Value");
ResponseMessageResult response = new ResponseMessageResult(responseMessage);
return response;
9
Dave Thompson
public static class HttpExtentions
{
    public static IHttpActionResult AddHeader(this IHttpActionResult action,
        string headerName, IEnumerable<string> headerValues)
    {
        return new HeaderActionResult(action, headerName, headerValues);
    }

    public static IHttpActionResult AddHeader(this IHttpActionResult action,
        string headerName, string header)
    {
        return AddHeader(action, headerName, new[] {header});
    }

    private class HeaderActionResult : IHttpActionResult
    {
        private readonly IHttpActionResult action;

        private readonly Tuple<string, IEnumerable<string>> header;

        public HeaderActionResult(IHttpActionResult action, string headerName,
            IEnumerable<string> headerValues)
        {
            this.action = action;

            header = Tuple.Create(headerName, headerValues);
        }

        public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = await action.ExecuteAsync(cancellationToken);

            response.Headers.Add(header.Item1, header.Item2);

            return response;
        }
    }
}
4
Andriy Tolstoy

Hier ist eine Lösung, die ich in meinem gebräuchlichen Web API 2-Bibliothekscode verwende, der das Setzen von Kopfzeilen oder anderen Eigenschaften von HttpResponseMessage, die in ExecuteAsync-- bereitgestellt werden, ohne Unterstützung an eine bestimmte abgeleitete NegotiatedContentResult-Implementierung unterstützen kann:

public class FlexibleNegotiatedContentResult<T> : NegotiatedContentResult<T>
{
    private readonly Action<HttpResponseMessage> _responseMessageDelegate;

    public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        : base(statusCode, content, contentNegotiator, request, formatters)
    {
    }

    public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, ApiController controller, Action<HttpResponseMessage> responseMessageDelegate = null)
        : base(statusCode, content, controller)
    {
        _responseMessageDelegate = responseMessageDelegate;
    }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage responseMessage = await base.ExecuteAsync(cancellationToken);

        if (_responseMessageDelegate != null)
        {
            _responseMessageDelegate(responseMessage);
        }

        return responseMessage;
    }
}

und ein Beispiel zur Verwendung:

new FlexibleNegotiatedContentResult<string>(HttpStatusCode.Created, "Entity created!", controller, response => response.Headers.Location = new Uri("https://myapp.com/api/entity/1"));
3
NathanAldenSr

Dies kann mit einem ActionFilterAttribute erreicht werden, das die Antwort nach der Controller-Funktion untersucht. Bevor sie jedoch ausgeht, können Sie das Attribut für die Controller-Methode festlegen, um diese Informationen hinzuzufügen.

public class EnableETag : ActionFilterAttribute
{

    /// <summary>
    /// NOTE: a real production situation, especially when it involves a web garden
    ///       or a web farm deployment, the tags must be retrieved from the database or some other place common to all servers.
    /// </summary>
    private static ConcurrentDictionary<string, EntityTagHeaderValue> etags = new ConcurrentDictionary<string, EntityTagHeaderValue>();

    public override void OnActionExecuting(HttpActionContext context)
    {
        var request = context.Request;
        if (request.Method == HttpMethod.Get)
        {
            var key = GetKey(request);
            ICollection<EntityTagHeaderValue> etagsFromClient = request.Headers.IfNoneMatch;
            if (etagsFromClient.Count > 0)
            {
                EntityTagHeaderValue etag = null;
                if (etags.TryGetValue(key, out etag) && etagsFromClient.Any(t => t.Tag == etag.Tag))
                {
                    context.Response = new HttpResponseMessage(HttpStatusCode.NotModified);
                    SetCacheControl(context.Response);
                }
            }
        }
    }
    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var request = context.Request;
        var key = GetKey(request);
        EntityTagHeaderValue etag;
        if (!etags.TryGetValue(key, out etag) || request.Method == HttpMethod.Put ||
        request.Method == HttpMethod.Post)
        {
            etag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\"");
            etags.AddOrUpdate(key, etag, (k, val) => etag);
        }
        context.Response.Headers.ETag = etag;
        SetCacheControl(context.Response);
    }
    private string GetKey(HttpRequestMessage request)
    {
        return request.RequestUri.ToString();
    }

    /// <summary>
    /// Defines the time period to hold item in cache (currently 10 seconds)
    /// </summary>
    /// <param name="response"></param>
    private void SetCacheControl(HttpResponseMessage response)
    {
        response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromSeconds(10),
            MustRevalidate = true,
            Private = true
        };
    }
}

}

3
PlexDM

sehr alte Frage. Wahrscheinlich machten andere Antworten zu dieser Zeit Sinn, aber heute können Sie diese Zeile einfach hinzufügen, ohne IHttpActionResult zu ändern oder zu erweitern. Es fügt Kopfzeile in Ihrer Antwort perfekt hinzu. Stellen Sie sicher, dass Sie auch den RFC 1123 -Standard wie unten beschrieben formatieren. Ansonsten ist der Client nicht in der Lage, sie mit HttpClient zu lesen, obwohl in den Kopfzeilen Last-Modified angezeigt wird.

    System.Web.HttpContext.Current.Response.Headers.
Add(Microsoft.Net.Http.Headers.HeaderNames.LastModified, DBdateModified.Value.ToString("r"));
0
batmaci