I've done a few searches but haven't seem to find anything...
Using WebApi, I would like to map an input parameter to a header value: e.g.
E.g. in controller:
public User GetUser(int id){
...
return user;
}
I want WebApi to map the id parameter to a header value (e.g. X-Auth: 1234)... rather than an URL parameter.
Is this supported?
I don't think this is supported out of the box, like for example with the [FromBody] attribute.
It seems you should be able to achieve this functionality by using Model Binders, as described here. In the model binder you have access to the request and its headers, so you should be able to read the header and set its value to the bindingContext.Model property.
Edit: Reading the article further, it seems a custom HttpParameterBinding and a ParameterBindingAttribute is a more appropriate solution, or at least I would go this way. You could implement a generic [FromHeader] attribute, which does the job. I am also fighting the same problem, so I will post my solution once I have it in place.
Edit 2: Here is my implementation:
public class FromHeaderBinding : HttpParameterBinding
{
private string name;
public FromHeaderBinding(HttpParameterDescriptor parameter, string headerName)
: base(parameter)
{
if (string.IsNullOrEmpty(headerName))
{
throw new ArgumentNullException("headerName");
}
this.name = headerName;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues(this.name, out values))
{
actionContext.ActionArguments[this.Descriptor.ParameterName] = values.FirstOrDefault();
}
var taskSource = new TaskCompletionSource<object>();
taskSource.SetResult(null);
return taskSource.Task;
}
}
public abstract class FromHeaderAttribute : ParameterBindingAttribute
{
private string name;
public FromHeaderAttribute(string headerName)
{
this.name = headerName;
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return new FromHeaderBinding(parameter, this.name);
}
}
public class MyHeaderAttribute : FromHeaderAttribute
{
public MyHeaderAttribute()
: base("MyHeaderName")
{
}
}
Then you can use it like this:
[HttpGet]
public IHttpActionResult GetItem([MyHeader] string headerValue)
{
...
}
Hope that helps.
WebApi on DotNet Core has a has some additional attributes for extracting data from the request. Microsoft.AspNetCore.Mvc.FromHeaderAttribute will read from the request head.
public ActionResult ReadFromHeader([FromHeader(Name = "your-header-property-name")] string data){
//Do something
}
Thank you filipov for the answer.. I took your code and modified it a bit to suit my needs. I am posting my changes here in case anyone can make use of this.
I made 2 changes.
I liked the idea of the FromHeaderAttribute, but without subclassing. I made this class public, and require the user to set the param name.
I needed to support other data types besides string. So I attempt to convert the string value to the descriptor's parameterType.
Use it like this:
[HttpGet]
public void DeleteWidget(long widgetId, [FromHeader("widgetVersion")] int version)
{
...
}
And this is my FromHeaderBinding
public class FromHeaderBinding : HttpParameterBinding
{
private readonly string _name;
public FromHeaderBinding(HttpParameterDescriptor parameter, string headerName)
: base(parameter)
{
if (string.IsNullOrEmpty(headerName)) throw new ArgumentNullException("headerName");
_name = headerName;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues(_name, out values))
{
var tempVal = values.FirstOrDefault();
if (tempVal != null)
{
var actionValue = Convert.ChangeType(tempVal, Descriptor.ParameterType);
actionContext.ActionArguments[Descriptor.ParameterName] = actionValue;
}
}
var taskSource = new TaskCompletionSource<object>();
taskSource.SetResult(null);
return taskSource.Task;
}
}
Related
I have a Web API method:
public List<Task> GetTasks([FromUri] TaskFilter filter)
{
}
The method has parameter with list of nullable identifiers:
public class TaskFilter
{
public IList<int?> Assignees { get; set; }
}
When I call it:
GET /tasks?assignees=null
Server returns an error:
{
"message":"The request is invalid.",
"modelState": {
"assignees": [ "The value 'null' is not valid for Nullable`1." ]
}
}
It works only if I pass empty string:
GET /tasks?assignees=
But standard query string converters (from JQuery, Angular, etc) do not work with nulls in such way.
How to make ASP.NET to interpret 'null' as null?
Upd: The query string can contain several identifiers, e.g.:
GET /tasks?assignees=1&assignees=2&assignees=null
Upd2: JQuery converts nulls in array to empty strings, and ASP.NET interprets them as null. So the question is about calling WebAPI from Angular 1.6 ($HttpParamSerializerProvider)
Upd3: I know about workarounds, but I do not ask for them. I want a solution for specific problem:
It is a GET method
Method accepts a list from Uri
A list can contain null values
It should be List<int?> because API docs are generated automatically, and I do not want to see text array as parameter type
By default ASP.NET expects empty strings for null values (JQuery.param works in that way)
But some client libraries (e.g. Angular) does not convert null array items to empty strings
You can create a custom model bind for this specific type, inherithing from DefaultModelBinder, for sample:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class TaskFilterBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var assignees = request.QueryString["assignees"];
if (assignees == "null") // check if assignees is null (string) then return NULL
return null;
return assignees;
}
}
Finally we need to inform the controller as to the binding we want it to use. This we can specify using attributes
[ModelBinder(typeof(TaskFilterBinder))]
as below:
public List<Task> GetTasks([FromUri(ModelBinder=typeof(TaskFilterBinder))] TaskFilter filter)
{
// Do your stuff.
}
For more reference check this link on Custom Model Binders.
Hope, this solves your problem . Thanks
Finally, I found a solution using custom value provider:
using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
using System.Web.Http.ValueProviders.Providers;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.ModelBinding;
public sealed class NullableValueProviderAttribute : ModelBinderAttribute
{
private readonly string[] _nullableColumns;
public NullableValueProviderAttribute(params string[] nullableColumns)
{
_nullableColumns = nullableColumns;
}
public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
return new ValueProviderFactory[] { new NullableValueProviderFactory(_nullableColumns) };
}
}
public class NullableValueProviderFactory : ValueProviderFactory, IUriValueProviderFactory
{
private readonly string[] _nullableColumns;
public NullableValueProviderFactory(string[] nullableColumns)
{
_nullableColumns = nullableColumns;
}
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
return new NullableQueryStringValueProvider(actionContext, CultureInfo.InvariantCulture, _nullableColumns);
}
}
public class NullableQueryStringValueProvider : NameValuePairsValueProvider
{
private static readonly string[] _nullValues = new string[] { "null", "undefined" };
private static IEnumerable<KeyValuePair<string, string>> GetQueryNameValuePairs(HttpRequestMessage request, string[] nullableColumns)
{
foreach (var pair in request.GetQueryNameValuePairs())
{
var isNull = Array.IndexOf(nullableColumns, pair.Key) >= 0 && Array.IndexOf(_nullValues, pair.Value) >= 0;
yield return isNull ? new KeyValuePair<string, string>(pair.Key, "") : pair;
};
}
public NullableQueryStringValueProvider(HttpActionContext actionContext, CultureInfo culture, string[] nullableColumns) :
base(GetQueryNameValuePairs(actionContext.ControllerContext.Request, nullableColumns), culture)
{ }
}
And specify it in Web API action:
public List<Task> GetTasks([NullableValueProvider("assignees")] TaskFilter filter)
{
}
I have a situation in which I would like to do custom parameter binding for an api controller in ASP.NET core.In WebAPI 2.0 it was possible to perform custom binding to primitive types by implementing various interfaces such as IValueProvider and providing a ValueProviderFactory. This does not seem the case with ASP.NET core in as far as what I understand from the documentation I found here.
I did notice this SO post which lead me to this article which overrides the behavior for the MutableObjectModelBinder. It would appear I could do something along those lines such as:
[HttpGet]
[Route("api/{domain}/[controller]")]
public IEnumerable<string> Get([ModelBinder(BinderType = typeof(MyCustomBinder))]string orderby)
{
//Do stuff here
}
This doesn't necessarily seem right to me since I am just dealing with a primitive type however I cannot seem to find any documentation for another way of doing this.
Create a binder provider class for your custom type
public class MyCustomBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(MyCustom))
{
return new BinderTypeModelBinder(typeof(MyCustomBinder));
}
return null;
}
}
and register it in the services
services.AddMvc(c =>
{
c.ModelBinderProviders.Insert(0, new MyCustomBinderProvider());
});
And the custom binder can go like
public class MyCustomBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(MyCustom))
{
return TaskCache.CompletedTask;
}
var parameters = new Dictionary<string, string>();
foreach (var parameter in bindingContext.ModelType.GetProperties())
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(parameter.Name);
if (valueProviderResult.FirstValue != null)
{
parameters.Add(parameter.Name, valueProviderResult.FirstValue);
}
}
var result = Activator.CreateInstance(bindingContext.ModelType);
//write your custom code to map the result with the parameters
bindingContext.Result = ModelBindingResult.Success(result);
return TaskCache.CompletedTask;
}
}
Your custom type class
[ModelBinder(BinderType = typeof(MyCustomBinder))]
public class MyCustom
{
public int Page { get; set; }
public int Rows { get; set; }
}
and your controller can take the custom class as query string parameter
[HttpGet("data")]
public DataTransferObject GetData(MyCustom query)
{
}
Migrating OP's solution from the question to an answer, with meta commentary trimmed:
I just decided to go with a helper class to parse the parameter due to having to meet deadlines.
Hy, I'm using Attribute Routing for my project and I don't know how I can take the value of the parameter from the URL. I tried using the Request but I can't find it anywhere.
When I make GET: http://localhost:60163/courses/courseId=1 how can I take the value of 1 for courseId?
[RoutePrefix("courses")]
public class CoursesController : ApiController
{
[Route("{courseId}")] //this is the value I need in the TeacherAuthenticationAttribute Action Filter
[TeacherAuthorizationRequiered]
public async Task<IHttpActionResult> GetCourse(int courseId=0)
{
Course course = await db.Courses.FindAsync(courseId);
if (course == null)
return NotFound();
return Ok(course);
}
In TeacherAuthorizationFilter I need the value of the courseId in order to validate it.
public class TeacherAuthorizationRequieredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (filterContext.Request.Headers.Contains(Token))
var courseIdValue = filterContext.Request.RequestUri.ParseQueryString()["courseId"];
}
}
Here is the problem, I don't know how I can get the value using the Request or if there is another way to do it. Thank you very much!
public class TeacherAuthorizationRequieredAttribute : ActionFilterAttribute
{
private const string Token = "Token";
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (filterContext.Request.Headers.Contains(Token))
var courseIdValue = filterContext.Request.RequestUri.ParseQueryString()["courseId"];
}
}
now , you have to added in request body
Token :"yourtoken"
I've got an ASP.NET MVC website that needs to display user-provided URLs stored in the DB. The way they're displayed will be different depending on how that URL would be routed if that URL refers to the website itself.
For example, supposing the website is foo.com:
URL stored in DB: foo.com/pie/3/nutrition
Controller is "pie"
Action is "nutrition"
ID is 3
The way the link is formatted depends on all three of these.
How would I extract this information correctly? Can I query the URL routing device?
Note: "Use a regular expression" type of answers don't interest me -- the site, action, or controller names could change, the website may be accessible through multiple site names and ports, etc...
You may find the RouteInfo class illustrated in this blog post useful:
public class RouteInfo
{
public RouteData RouteData { get; private set; }
public RouteInfo(Uri uri, string applicationPath)
{
RouteData = RouteTable.Routes.GetRouteData(new InternalHttpContext(uri, applicationPath));
}
private class InternalHttpContext : HttpContextBase
{
private readonly HttpRequestBase request;
public InternalHttpContext(Uri uri, string applicationPath)
{
this.request = new InternalRequestContext(uri, applicationPath);
}
public override HttpRequestBase Request
{
get { return this.request; }
}
}
private class InternalRequestContext : HttpRequestBase
{
private readonly string appRelativePath;
private readonly string pathInfo;
public InternalRequestContext(Uri uri, string applicationPath)
{
this.pathInfo = uri.Query;
if (string.IsNullOrEmpty(applicationPath) || !uri.AbsolutePath.StartsWith(applicationPath, StringComparison.OrdinalIgnoreCase))
{
this.appRelativePath = uri.AbsolutePath.Substring(applicationPath.Length);
}
else
{
this.appRelativePath = uri.AbsolutePath;
}
}
public override string AppRelativeCurrentExecutionFilePath
{
get { return string.Concat("~", appRelativePath); }
}
public override string PathInfo
{
get { return this.pathInfo; }
}
}
}
You could use it like this:
public ActionResult Index()
{
Uri uri = new Uri("http://foo.com/pie/3/nutrition");
RouteInfo routeInfo = new RouteInfo(uri, this.HttpContext.Request.ApplicationPath);
RouteData routeData = routeInfo.RouteData;
string controller = routeData.GetRequiredString("controller");
string action = routeData.GetRequiredString("action");
string id = routeData.Values["id"] as string;
...
}
From the section about unit testing in this: scott guthrie blog post
you can do something like this:
MockHttpContext httpContxt = new MockHttpContext("foo.com/pie/3/nutrition");
RouteData routeData = new routes.GetRouteData(httpContext);
where routes is the RouteCollection you used to initialize your routes in your application. Then you can interrogate routeData["controller"] etc
the post is about an early version of MVC, so the class names may have changed since then.
I am trying to create a custom attribute in mvc to use it's parameters in a view as breadCrumb.
well, this is the code of the attribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BreadCrumbAttribute : Attribute {
public BreadCrumbAttribute(string title, string parent, string url) {
this._title = title;
this._parent = parent;
this._url = url;
}
#region named parameters properties
private string _title;
public string Title {
get { return _title; }
}
private string _url;
public string Url {
get { return _url; }
}
private string _parent;
public string Parent {
get { return _parent; }
}
#endregion
#region positional parameters properties
public string Comments { get; set; }
#endregion
}
this is the call of the attribute
[BreadCrumbAttribute("tile", "parent name", "url")]
public ActionResult Index() {
//code goes here
}
this is a way of how I'd like to get the values. (this is a partial view)
System.Reflection.MemberInfo inf = typeof(ProductsController);
object[] attributes;
attributes = inf.GetCustomAttributes(typeof(BreadCrumbAttribute), false);
foreach (Object attribute in attributes) {
var bca = (BreadCrumbAttribute)attribute;
Response.Write(string.Format("{0}><a href={1}>{2}</a>", bca.Parent, bca.Url, bca.Title));
}
Unfortunately, the attribute didn't get call with the way I implement it. Although, If I add the attribute in Class instead of an Action method it worked.
How could I make it work?
Thanks
The problem is that you are using reflection to get the attributes for the class, so naturally it does not include attributes defined on the action method.
To get those, you should define an ActionFilterAttribute, and in the OnActionExecuting or OnActionExecuted method, you can use filterContext.ActionDescriptor.GetCustomAttributes() method (MSDN description here).
Note that with this solution, you will likely have two different types of attributes: The first one is the one you wrote, to define the breadcrumbs. The second is the one that looks at the attributes on the executing action and builds up the breadcrumb (and presumably adds it to the ViewModel or sticks it in HttpContext.Items or something).