I need to use a custom modelbinder of some kind to always treat incoming dates in UK format, i have setup a custom model binder and registered like so:
GlobalConfiguration.Configuration.BindParameter(typeof(DateTime), new DateTimeModelBinder());
This only seems to work for querystring parameters, and only works if i specify [ModelBinder] on my parameter like so, is there i way to make all actions use my model binder:
public IList<LeadsLeadRowViewModel> Get([ModelBinder]LeadsIndexViewModel inputModel)
Also, how can i get my posted form to my Api controller to use my model binder?
You can register a model binder globally by implementing a ModelBinderProvider and inserting it into the list of services.
Example use in Global.asax:
GlobalConfiguration.Configuration.Services.Insert(typeof(ModelBinderProvider), 0, new Core.Api.ModelBinders.DateTimeModelBinderProvider());
Below is example code demonstrating a ModelBinderProvider and a ModelProvider implemenation that converts DateTime arguments in a culture aware manner (using the current threads culture);
DateTimeModelBinderProvider implementation:
using System;
using System.Web.Http;
using System.Web.Http.ModelBinding;
...
public class DateTimeModelBinderProvider : ModelBinderProvider
{
readonly DateTimeModelBinder binder = new DateTimeModelBinder();
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
if (DateTimeModelBinder.CanBindType(modelType))
{
return binder;
}
return null;
}
}
DateTimeModelBinder implementation:
public class DateTimeModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
ValidateBindingContext(bindingContext);
if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) ||
!CanBindType(bindingContext.ModelType))
{
return false;
}
bindingContext.Model = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName)
.ConvertTo(bindingContext.ModelType, Thread.CurrentThread.CurrentCulture);
bindingContext.ValidationNode.ValidateAllProperties = true;
return true;
}
private static void ValidateBindingContext(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
if (bindingContext.ModelMetadata == null)
{
throw new ArgumentException("ModelMetadata cannot be null", "bindingContext");
}
}
public static bool CanBindType(Type modelType)
{
return modelType == typeof(DateTime) || modelType == typeof(DateTime?);
}
}
I think you don't need a model binder. Your approach is incorrect. The right approach for dates is using a client side globalization library like the globalize library to parse dates formatted in any language and transform them into JavaScript date objects. Then you can serialize your datascript data structures in JSON with the browser JSON.stringify, and this should work.
It is better to use always standard formats for dates and to use a formatter instead than a model binder. Available formatters handle also TimeZones correctly, if you use the kind parameter of your C# DateTime objects to specify if the date time is expressed in local time or in UTC time.
Attribute routing seems to conflict with model binding. If you use attribute routing, you can wrap the global.asax configuration into a single GlobalConfiguration.Config call to avoid the initialisation issues:
GlobalConfiguration.Configure(config =>
{
config.BindParameter(typeof(DateTime), new DateTimeModelBinder());
config.MapHttpAttributeRoutes();
}
(This might be fixed in the upcoming ASP.NET 5 if it's related to bug #1165.)
You do not need a custom model binder for that. You should be good by changing the thread culture to UK or set your web.config settings for UK, if this is what your site is using all the time.
If not, you can still change the DateTimeFormatInfo for the CurrentCulture to UK one.
There is also a good post about model binders available here by Scott Hanselman.
You can put this in Global.asax:
ModelBinders.Binders[typeof(DateTime)] = new DateAndTimeModelBinder()
Related
I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.
However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.
Here is how my controller looks like now:
[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
//some endpoints here
}
ApiKeyAuthorize Class:
public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
//////This...
private readonly IOptions<MyConfig> _config;
public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
{
_config = config;
}
/////////...is what I am trying to accomplish
public void OnAuthorization(AuthorizationFilterContext context)
{
var request = context.HttpContext.Request;
var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);
if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
{
context.Result = new UnauthorizedResult();
}
}
}
My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.
Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:
[ServiceFilter(typeof(ApiAuthorizeAttribute))]
This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:
[ApiAuthorizeAttribute(Roles = "Admin")]
It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.
I'm working on a .NET Core 2 API project and have been trying to implement a universal string trim model binder that would trim all string values of provided request parameters and field values. So far I have had mixed results and am struggling to find working example that would point me in the right direction. I've been trying to implement the same model binder as posted by Vikash Kumar.
This model binder works fine for all string values that are passed into controller actions via direct parameters, such as public IActionResult Profile(string username), but for string fields in complex objects the BindModelAsync method of the TrimmingModelBinder class never gets called. An example of an HttpPost action in my controller would be public IActionResult Profile([FormBody] ProfileLookupModel model). The model binder does not seem to check the fields of the complex model. It also doesn't work for fields that are Lists of strings.
I recall prior to .NET Core, specifying a string trim model binder would recursively check each field of complex models, even models within complex models. This doesn't seem to be the case in .NET Core, but I might be wrong. My project is targeting the netcoreapp2.0 framework.
I'm curious if anyone has had the same issue as me and possibly found a solution for it.
Note: I haven't posted any sample code as it is the same as the code from the referenced article.
I'll add my 2 cents here. Instead of using some kind of model binding hook, I went to a action filter. One of the advantages is that the developer can select which actions to use, instead of having this processing for all requests and model bindings (not that it should affect performance that much). Btw action filters can also be applied globally.
Here is my code, first create an action filter.
public class TrimInputStringsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
foreach (var arg in context.ActionArguments.ToList())
{
if (arg.Value is string)
{
if (arg.Value == null)
{
continue;
}
string val = arg.Value as string;
if (!string.IsNullOrEmpty(val))
{
context.ActionArguments[arg.Key] = val.Trim();
}
continue;
}
Type argType = arg.Value.GetType();
if (!argType.IsClass)
{
continue;
}
TrimAllStringsInObject(arg.Value, argType);
}
}
private void TrimAllStringsInObject(object arg, Type argType)
{
var stringProperties = argType.GetProperties()
.Where(p => p.PropertyType == typeof(string));
foreach (var stringProperty in stringProperties)
{
string currentValue = stringProperty.GetValue(arg, null) as string;
if (!string.IsNullOrEmpty(currentValue))
{
stringProperty.SetValue(arg, currentValue.Trim(), null);
}
}
}
}
To use it, either register as global filter or decorate your actions with the TrimInputStrings attribute.
[TrimInputStrings]
public IActionResult Register(RegisterViewModel registerModel)
{
// Some business logic...
return Ok();
}
TrimmingModelBinder is essentially configured for strings only, and defaults back to SimpleTypeModelBinder if it fails, or other binders configured. So if your implementation is essentially the same as in TrimmingModelBinder then it will definitely work for strings only.
For complex types, I recommend creating a new binder, and its corresponding provider, which will have to check all string properties in the model type and trim the value before binding. Then register this binder at index 0 such that its the first one checked before any other binders are tried.
services.AddMvc(options => option.ModelBinderProviders.Insert(0, new MyComplexTypeModelBinderProvider());
I want to bind an interface model from my action method with a request that the content-type is application/json. I'm using the [FromBody] attribute in my action method.
I tried to create a custom modelBinder derived from ComplexTypeModelBinder, by following this link: Custom Model Binding in Asp.net Core, 3: Model Binding Interfaces, but it doesn't work, my model is always null. I learned after that when you use the atribute [FromBody] the BodyModelBinder is called and internally is calling JsonInputFormatter and it doesn't use the custom modelBinder.
I'm looking for a way to bind my interface model. I can use the MVC DI to map each interface with its implementation. My action method is defined as :
public async Task<IActionResult> Create(IOperator user)
{
if (user == null)
{
return this.BadRequest("The user can't not be null");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
IOperator op = await this.AuthenticationFrontService.CreateOperatorAsync(user.Login, user.Password, user.FirstName, user.LastName, user.ValidUntil, user.Role, user.Comment);
return new CreatedAtActionResult("Get", "operators", new { id = ((Operator)op).Id }, op);
}
I tried another solution by using the MetadataType attribute in my interface but it doesn't exist in the namespace System.ComponentModel.DataAnnotations and i read that asp.net core mvc doesn't use this attribute Asp.Net MVC MetaDataType Attribute not working. I don't want to install the package microsoft.aspnetcore.mvc.dataannotations in domain model project to use the ModelDataType attribute.
I tried another solution by creating a custom JsonInputFormater in other words i derived the class JsonInputFormatter and by analyzing the source code, i've found that the JsonSerializer couldn't deserialize an interface which is logical. So i'm looking for a solution where i could custom the jsonserializer maybe by using a resolver or a generic converter.
Any help will greatly appreciated.
Thanks.
Using an interface is fine for a C# method, but MVC needs to know what concrete type it should instantiate when calling an Action since it's creating it. It doesn't know what type to use, so it can't bind input from Form/QueryString/etc to. Create a very basic model for use in your action that does nothing but implement your interface IOperator, if your goal was to keep it slim, and set that to your Action parameter and it should work fine.
I have tried using an interface on a action as well, and through my own searching, I found no way to get it to work, other than just using classes instead of interfaces to bind to.
public class Operator : IOperator
{
//Implement interface
}
.
public async Task<IActionResult> Create(Operator user)
{
if (user == null)
{
return this.BadRequest("The user can't not be null");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
IOperator op = await this.AuthenticationFrontService.CreateOperatorAsync(user.Login, user.Password, user.FirstName, user.LastName, user.ValidUntil, user.Role, user.Comment);
return new CreatedAtActionResult("Get", "operators", new { id = ((Operator)op).Id }, op);
}
Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.
My application is in WCF of xml transformation. now need to change to integrate with MEF. which is the best way to implement MEF or which type of architecture should i use to implement with less effort and less change in existing code?
EDIT
Explanation:
I have four hotel xml transformation
in wcf service. At one end it is fixed
format xml and another end different
xml format for each new hotel.and
another 20 hotel work will come. for
this repetative work i need some
re-usable and extendable architecture.
i want to convert existing
architecture upgrade with MEF for
future perspective. so i can do better
for next 20 hotel xml transformation.
How are you doing your Xml transformation? Is it through code, or XSLT?
If through code, I would define an IXmlTranslator that converts your xml into a common model:
public interface IXmlTranslator
{
XmlModel Translate(XElement element);
}
Where XmlModel is your common model:
public class XmlModel
{
// Properties
}
You'd need to specifically know which translator to use, so you'd need to pass in some sort of metadata, so we'll define a name:
public interface INamedMetadata
{
string Name { get; }
}
So an example translator could look like:
[Export(typeof(IXmlTranslator),
ExportMetadata("Name", "Null")]
public class NullXmlTranslator : IXmlTranslator
{
public XmlModel Translate(XElement element)
{
return null;
}
}
MEF will take care of projecting your metadata into an instance of INamedMetadata. Next, create a service which consumes IXmlTranslators:
[Export]
public class XmlTranslatorService
{
private readonly IEnumerable<Lazy<IXmlTranslator, INamedMetadata>> _translators;
[ImportingConstructor]
public XmlTranslatorService(IEnumerable<Lazy<IXmlTranslator, INamedMetadata>> translators)
{
_translators = translators;
}
public XmlModel Translate(string name, XElement element)
{
var translator = GetTranslator(name);
if (translator == null)
throw new ArgumentException("No translator is available to translate the target xml.");
return translator.Translate(element);
}
private IXmlTranslator GetTranslator(string name)
{
var translator = _translators
.Where(t => t.Metadata.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
.Select(t => t.Value)
.FirstOrDefault();
return translator;
}
}
I've made the enumerable of available translators part of the constructor arguments, as it defines dependencies that are required for the service to work. MEF will take care of injecting this enumerable at composition time.
What you need to do, is either Import an instance of the XmlTranslatorService into whatever class you want to use it from, or you can initialise an instance directly from your CompositionContainer, e.g.:
var service = container.GetExportedValue<XmlTranslatorService>();
The only thing remaining would be
Creating specialised translators for each of the hotel types into the common XmlModel model class.
Serialisation of the XmlModel class into the target xml.
Hope that points you in the right direction?