Last active
November 1, 2023 06:20
-
-
Save 0xced/d679d43bb7dd8a9ab805db43e561e48d to your computer and use it in GitHub Desktop.
[FromHeader] parameter binding and attribute for ASP.NET Web API + Swashbuckle integration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Http; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Web.Http; | |
using System.Web.Http.Controllers; | |
using System.Web.Http.Metadata; | |
namespace Gist | |
{ | |
// Adapted from https://stackoverflow.com/questions/20618900/webapi-mapping-parameter-to-header-value/20653775#20653775 | |
public class FromHeaderBinding : HttpParameterBinding | |
{ | |
private readonly string _name; | |
public FromHeaderBinding(HttpParameterDescriptor parameter, string headerName) : base(parameter) | |
{ | |
if (string.IsNullOrEmpty(headerName)) | |
{ | |
throw new ArgumentNullException(nameof(headerName)); | |
} | |
_name = headerName; | |
} | |
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) | |
{ | |
if (actionContext.Request.Headers.TryGetValues(_name, out var values)) | |
{ | |
var converter = TypeDescriptor.GetConverter(Descriptor.ParameterType); | |
try | |
{ | |
actionContext.ActionArguments[Descriptor.ParameterName] = converter.ConvertFromString(values.FirstOrDefault()); | |
} | |
catch (Exception exception) | |
{ | |
var error = new HttpError("The request is invalid.") { MessageDetail = exception.Message }; | |
throw new HttpResponseException(actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, error)); | |
} | |
} | |
else if (Descriptor.IsOptional) | |
{ | |
actionContext.ActionArguments[Descriptor.ParameterName] = Descriptor.DefaultValue ?? Activator.CreateInstance(Descriptor.ParameterType); | |
} | |
else | |
{ | |
var error = new HttpError("The request is invalid.") { MessageDetail = $"The `{_name}` header is required." }; | |
throw new HttpResponseException(actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, error)); | |
} | |
return Task.FromResult<object>(null); | |
} | |
} | |
public abstract class FromHeaderAttribute : ParameterBindingAttribute | |
{ | |
public string HeaderName { get; } | |
protected FromHeaderAttribute(string headerName) | |
{ | |
HeaderName = headerName; | |
} | |
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) | |
{ | |
return new FromHeaderBinding(parameter, HeaderName); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using System.Web.Http.Description; | |
using Swashbuckle.Swagger; | |
namespace Gist | |
{ | |
// Adapted from http://analogcoder.com/2015/11/how-to-create-header-using-swashbuckle/ | |
public class FromHeaderAttributeOperationFilter : IOperationFilter | |
{ | |
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) | |
{ | |
foreach (var httpParameterDescriptor in apiDescription.ActionDescriptor.GetParameters().Where(e => e.GetCustomAttributes<FromHeaderAttribute>().Any())) | |
{ | |
var parameter = operation.parameters.Single(p => p.name == httpParameterDescriptor.ParameterName); | |
parameter.name = httpParameterDescriptor.GetCustomAttributes<FromHeaderAttribute>().Single().HeaderName; | |
parameter.@in = "header"; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Web.Http; | |
using Swashbuckle.Application; | |
namespace Gist | |
{ | |
public static class SwaggerConfig | |
{ | |
public static void Register(HttpConfiguration config) | |
{ | |
config.EnableSwagger(c => | |
{ | |
c.SingleApiVersion("v1", "Sample API"); | |
c.OperationFilter(() => new FromHeaderAttributeOperationFilter()); | |
}) | |
.EnableSwaggerUi(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Web.Http; | |
namespace Gist | |
{ | |
public class FromExampleHeaderAttribute : FromHeaderAttribute | |
{ | |
public FromExampleHeaderAttribute() : base("X-Example") {} | |
} | |
[RoutePrefix("api/Example")] | |
public class ExampleController : ApiController | |
{ | |
[HttpGet] | |
[Route("OptionalString")] | |
public IHttpActionResult EchoExampleHeaderOptional([FromExampleHeader] string example = "default") | |
{ | |
return Ok(new { example }); | |
} | |
[HttpGet] | |
[Route("RequiredString")] | |
public IHttpActionResult EchoExampleHeaderRequired([FromExampleHeader] string example) | |
{ | |
return Ok(new { example }); | |
} | |
[HttpGet] | |
[Route("OptionalInt")] | |
public IHttpActionResult EchoExampleHeaderOptional([FromExampleHeader] int id = 0) | |
{ | |
return Ok(new { id }); | |
} | |
[HttpGet] | |
[Route("RequiredInt")] | |
public IHttpActionResult EchoExampleHeaderRequired([FromExampleHeader] int id) | |
{ | |
return Ok(new { id }); | |
} | |
[HttpGet] | |
[Route("OptionalNullableDateTimeOffset")] | |
public IHttpActionResult EchoExampleHeaderOptional([FromExampleHeader] DateTimeOffset? date = null) | |
{ | |
return Ok(new { date = date ?? DateTimeOffset.Now }); | |
} | |
[HttpGet] | |
[Route("OptionalDateTimeOffset")] | |
public IHttpActionResult EchoExampleHeaderOptional([FromExampleHeader] DateTimeOffset date = default(DateTimeOffset)) | |
{ | |
return Ok(new { date }); | |
} | |
[HttpGet] | |
[Route("RequiredDateTimeOffset")] | |
public IHttpActionResult EchoExampleHeaderRequired([FromExampleHeader] DateTimeOffset date) | |
{ | |
return Ok(new { date }); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment