Une question simple, et je suis sûr que sa réponse est simple mais que je ne la trouve pas.
J'utilise WebAPI et je voudrais renvoyer un en-tête personnalisé à toutes les réponses (date/heure du serveur demandée par un dev pour la synchronisation).
Je lutte actuellement pour trouver un exemple clair illustrant comment, à un endroit (via Global.asax ou un autre emplacement central), je peux obtenir un en-tête personnalisé à afficher pour toutes les réponses.
Réponse acceptée, voici mon filtre (à peu près le même) et la ligne que j’ai ajoutée à la fonction d’enregistrement de la config WebApi.
NOTE: Le truc DateTime est NodaTime, aucune raison réelle ne l'a simplement intéressée.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("ServerTime", Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()).ToString());
}
Ligne de configuration:
config.Filters.Add(new ServerTimeHeaderFilter());
Pour cela, vous pouvez utiliser un ActionFilter personnalisé (System.Web.Http.Filters
)
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
}
}
Vous pouvez ensuite appliquer le filtre à toutes les actions de votre contrôleur en l'ajoutant à la configuration dans Global.asax, par exemple:
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());
Vous pouvez également appliquer l'attribut filter à l'action souhaitée sans la ligne de configuration globale.
Les réponses précédentes à cette question n'indiquent pas quoi faire si votre action du contrôleur lève une exception. Il y a deux moyens de base pour que cela fonctionne:
Ajouter un filtre d'exception :
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class HeaderAdderExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
context.Response.Content.Headers.Add("header", "value");
}
}
et dans votre configuration WebApi:
configuration.Filters.Add(new HeaderAdderExceptionFilter());
Cette approche fonctionne car le gestionnaire d'exceptions par défaut de WebApi envoie le HttpResponseMessage créé dans un filtre au lieu de créer le sien.
Remplace le gestionnaire d'exceptions par défaut:
using System.Net;
using System.Net.Http;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
public class HeaderAdderExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
HttpResponseMessage response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
response.Headers.Add("header", "value");
context.Result = new ResponseMessageResult(response);
}
}
et dans votre configuration WebApi:
configuration.Services.Replace(typeof(IExceptionHandler), new HeaderAdderExceptionHandler());
Vous ne pouvez pas utiliser les deux ensemble. D'accord, vous pouvez le faire, mais le gestionnaire ne fera jamais rien car le filtre a déjà converti l'exception en réponse.
Super important de noter que tel qu’écrit, ce code enverra tous les détails de l’exception au client. Vous ne voulez probablement pas faire cela en production, alors vérifiez toutes les surcharges disponibles sur CreateErrorResponse () et choisissez celle qui correspond à vos besoins.
La réponse de Julian m'a amené à créer le filtre, mais uniquement à l'aide de l'espace de noms System.Web (v4) et System.Web.Http (v5) (les packages MVC ne faisaient pas partie de ce projet particulier sur lequel il était utilisé.)
using System.Web;
using System.Web.Http.Filters;
...
public class AddCustomHeaderActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
actionExecutedContext.ActionContext.Response.Headers.Add("name", "value");
}
}
Et ajoutez-le à la global.asax pour l’utiliser sur chaque contrôleur/action
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());
Aucune des deux solutions ci-dessus n'a fonctionné pour moi. Ils ne compileraient même pas. Voici ce que j'ai fait. Ajoutée:
filters.Add(new AddCustomHeaderFilter());
à la méthode RegisterGlobalFilters(GlobalFilterCollection filters)
dans FiltersConfig.cs, puis ajouté
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
actionExecutedContext.HttpContext.Response.Headers.Add("ServerTime", DateTime.Now.ToString());
}
}
Selon mon exigence, la ligne de code ci-dessous sert à cela.
System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")
Cela peut être fait facilement par le gestionnaire de messages, il gérera à la fois une réponse correcte et un cas exceptionnel.
public class CustomHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// add header to request if you want
var response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("cutomKey", "cutomValue");
return response;
}
}
Ajoutez-le dans la config
config.MessageHandlers.Add(new CustomHeaderHandler());
J'ai combiné les chemins normal et d'exception dans une classe:
public class CustomHeaderAttribute : FilterAttribute, IActionFilter, IExceptionFilter
{
private static string HEADER_KEY { get { return "X-CustomHeader"; } }
private static string HEADER_VALUE { get { return "Custom header value"; } }
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return (new CustomHeaderAction() as IActionFilter).ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
}
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
return (new CustomHeaderException() as IExceptionFilter).ExecuteExceptionFilterAsync(actionExecutedContext, cancellationToken);
}
private class CustomHeaderAction: ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
{
actionExecutedContext.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
private class CustomHeaderException : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
}
context.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
Rien de spécial, mais au moins, ça me donne un endroit pour contrôler mes en-têtes supplémentaires. Pour le moment, il ne s'agit que de contenu statique, mais vous pouvez toujours le relier à une sorte de générateur/usine de dictionnaires.
J'ai eu le même problème en essayant d'ajouter un nouvel en-tête à l'ensemble du contrôleur, ajoutez simplement "services.AddHttpContextAccessor ();" à startup.cs puis créez votre contrôleur
public class EnController : Controller{
public EnController(IHttpContextAccessor myHttpAccessor)
{
myHttpAccessor.HttpContext.Response.Headers.Add("Content-Language", "en-US");
}
... more methods here...
}