How do I do a custom modelbinder when binding from body? - asp.net

I've been trying to experiment with model binding to make our API easier to use. When using the API I can't get the model binding to bind when the data is in the body, only when it is part of the query.
The code I have is:
public class FunkyModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var model = (Funky) bindingContext.Model ?? new Funky();
var hasPrefix = bindingContext.ValueProvider
.ContainsPrefix(bindingContext.ModelName);
var searchPrefix = (hasPrefix) ? bindingContext.ModelName + "." : "";
model.Funk = GetValue(bindingContext, searchPrefix, "Funk");
bindingContext.Model = model;
return true;
}
private string GetValue(ModelBindingContext context, string prefix, string key)
{
var result = context.ValueProvider.GetValue(prefix + key);
return result == null ? null : result.AttemptedValue;
}
}
When looking at the ValueProvider property on the bindingContext I only see QueryStringValueProvider and RouteDataValueProvider which I think means that if the data is in the body I won't get it. How should I do this? I would like to support posting data as either json or form-encoded.

I am looking into this as well.
WebApis Model Binder comes with two built in ValueProviders.
QueryStringValueProviderFactory & RouteDataValueProviderFactory
Which are searched when you call
context.ValueProvider.GetValue
This question has some code on how to bind data from the body.
how to pass the result model object out of System.Web.Http.ModelBinding.IModelBinder. BindModel?
You could create a custom ValueProvider to do this as well, probably a better idea - which will be searched for the value matching the key. The above link just does this within the model binder, which limits the ModelBinder to looking only in the body.
public class FormBodyValueProvider : IValueProvider
{
private string body;
public FormBodyValueProvider ( HttpActionContext actionContext )
{
if ( actionContext == null ) {
throw new ArgumentNullException( "actionContext" );
}
//List out all Form Body Values
body = actionContext.Request.Content.ReadAsStringAsync().Result;
}
// Implement Interface and use code to read the body
// and find your Value matching your Key
}

Related

Constraints on parameters in api interface

I've declared an API call in an interface and was wondering if it is possible to put constraints on some of the parameters. The API I'm accessing has these constraints as well and would like to enforce them in my program.
#GET("/recipes/search")
Call<RecipeResponse> getRecipes(
#Query("cuisine") String cuisine,
#Query("diet") String diet,
#Query("excludeIngredients") String excludeIngredients,
#Query("intolerances") String intolerances,
#Query("number") Integer number,
#Query("offset") Integer offset,
#Query("query") String query,
#Query("type") String type
);
How can I do this?
I know that it is possible to do this with POST request, and passing along an object via the RequestBody through the #Body annotation. Can I do this with a GET request too, where information is passed via the query string?
Thanks!
I think I ended up finding a solution. I've made a class SearchRecipeRequest in which I declare all possible parameters as class variables. In the setters I do the data validation such as checking for null on parameters that are required, or min/max value constraints on integers as specified by the endpoint. I then made a SearchRecipeRequestBuilder class to build such an object like so to make it easier to deal with all those possible parameters:
public class SearchRecipeRequestBuilder {
private String _cuisine = null,
_diet = null,
_excludeIngredients = null,
_intolerances = null,
_query = null,
_type = null;
private Integer _number = null,
_offset = null;
public SearchRecipeRequestBuilder() {}
public SearchRecipeRequest buildRequest() {
return new SearchRecipeRequest(_cuisine, _diet, _excludeIngredients, _intolerances, _number, _offset, _query, _type);
}
public SearchRecipeRequestBuilder cuisine(String cuisine) {
_cuisine = cuisine;
return this;
}
public SearchRecipeRequestBuilder diet(String diet) {
_diet = diet;
return this;
}
public SearchRecipeRequestBuilder excludeIngredients(String excludeIngredients) {
_excludeIngredients = excludeIngredients;
return this;
}
public SearchRecipeRequestBuilder intolerances(String intolerances) {
_intolerances = intolerances;
return this;
}
public SearchRecipeRequestBuilder query(String query) {
_query = query;
return this;
}
public SearchRecipeRequestBuilder type(String type) {
_type = type;
return this;
}
public SearchRecipeRequestBuilder number(Integer number) {
_number = number;
return this;
}
public SearchRecipeRequestBuilder offset(Integer offset) {
_offset = offset;
return this;
}
}
Which allows me to build the request like so:
SearchRecipeRequest request = new SearchRecipeRequestBuilder()
.query("burger")
.buildRequest();
I then pass along that object to a different function that knows how to use the request object to pass it along to the API.
That's how I'm doing it right now, if someone has a better way I'd love to hear it. :)
I got the idea to use the Builder pattern from a different StackOverflow question: Managing constructors with many parameters in Java.

ASP.NET MVC Conditional ViewModel Abstraction

I am new to ASP.NET MVC and I am stuck on a point. I am working on a classified site. My situation is, I have a lot of categories in which a user can post their ads and each ad category have different View. I have created a Controller Action like
public ActionResult PostAd(string CategoryName, string SubCategoryName)
{
if(categoryName == "Vehicle" && SubCategoryName == "Cars")
{
var model = new CarAdViewModel();
// set CarAdViewModel properties...
return View("CarAdCreateView", model);
}
else if(categoryName == "Vehicle" && SubCategoryName == "Bikes")
{
var model = new BikeAdViewModel();
// set BikeAdViewModel properties...
return View("BikeAdViewModel", model);
}
else if(categoryName == "Property" && SubCategoryName == "RentHouse")
{
var model = new RentHouseAdViewModel();
// set RentHouseAdViewModel properties...
return View("RentHouseAdViewModel", model);
}
else................... so on and so on
}
My problem is I have huge number of Categories and Sub Categories almost 60+. And if I keep on coding like above for 60+ categories and subcategories, my PostAd method is going to blast and become unmanageable.
Please tell me some best practice or pattern which can bring me out of this problem.
Unfortunately, some of what you are doing cannot be avoided. There needs to be some form of model and view selection based on category.
Use a factory pattern. Create a base class:
public abstract class BaseCategory
{
public abstract string GetViewName();
public abstract Object CreateModelFromFormData();
}
For each category, create a sub-class derived from BaseCategory and implement the abstract functions.
In your action, do the following:
public ActionResult PostAd(string categoryName, string subCategoryName)
{
BaseFactory factory;
if (categoryName == "Vehicle")
{
if (subCategoryName == "Cars")
{
factory = new CarsFactory();
}
else ...
}
else ...
return View(factory.GetViewName(), factory.CreateModelFromFormData());
}
I have a couple reasons for this schema:
I am purposefully using if/else for the factory selection. Your controller is going to be created and re-created for every action call. So pre-populating a list will constantly and needlessly create objects for categories that will not be selected. A simple if/else will be more efficient. If you want to prevent the if/else, you can put your factories in a Dictionary and select based on the categories, but that would be a lot of needless constructor actions.
I made the CreateModelFromFormData a function because I assume you'll need to copy data from the posted form data. This may require passing in data, but I left the function parameterless.
I used base/derived classes because the copying of the form data will probably need to be custom from the model being created and the form data being posted. Also, saving to persistent storage (file or database) may be category-specific as well.
It would be one of some possible solutions
public class PostAdData
{
public string CategoryName;
public string SubCategoryName;
public string ViewName;
public Type Model;
}
public class PostController : Controller
{
private readonly List<PostAdData> _theData;
public HomeController()
{
_theData = InitializeData();
}
public ActionResult PostAd(string categoryName, string subCategoryName)
{
var data = _theData.FirstOrDefault(c => c.CategoryName == categoryName && c.SubCategoryName == subCategoryName);
if (data != null)
{
var model = Activator.CreateInstance(data.Model);
return View(data.ViewName, model);
}
return View("Error");
}
[NonAction]
public List<PostAdData> InitializeData()
{
var result = new List<PostAdData>
{
new PostAdData
{
CategoryName = "Vehicle",
SubCategoryName = "Cars",
ViewName = "CarAdCreateView",
Model = typeof (CarAdViewModel)
}
};
return result;
}
}
You should make this data driven. You create a lookup table that has a compound primary key of category and subcategory. Then it has a table with View in it. Then you simply ad rows for each category/subcategory/view combination.
If you absolutely don't want a database, then you can use a simple hashset or dictionary.
var views = new Dictionary<Tuple<string,string>,string>();
views.Add(new Tuple<string,string>("Vehicle", "Cars"), "CarAdCreateView");
Then in your PostAd you just lookup the correct view.
What a beautiful solution on www.asp.net to my question, here is the link : http://forums.asp.net/t/1923868.aspx/1?ASP+NET+MVC+Conditional+ViewModel+Abstraction
Edit:
My code is :
public class AdsController : Controller
{
private readonly IAdService _adService;
public AdsController(IAdService adService)
{
_adService = adService;
}
public ActionResult PostAd(string Category, string SubCategory)
{
//Here I will call
var strategy = GetStrategy(CategoryName, SubCategoryName);
strategy.FillModel(_adService );
return View(strategy.ViewName, strategy.Model);
}
}

How do you make a class method modify itself?

asp.net C#4
I have a simple class to working with query strings.
A new instance is created like this:
public QueryString(string querystring)
{
try
{
_table = new Hashtable();
if (querystring.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
_table.Add(item[0].ToLower(), item[1]);
}
}
}
catch (Exception)
{
}
}
I want to add a method to this that will remove a key value pair. I don't want it to return a new querystring, I just want it to remove the pair from the current instance. Not sure how to do that since it says I can't assign a value to 'this'
public void Remove(string key)
{
String querystring = this.ToString();
try
{
_table = new Hashtable();
if (key.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
if (item[0] != key)
{
_table.Add(item[0].ToLower(), item[1]);
}
}
this = _table;
}
}
catch (Exception)
{
}
}
You're overcomplicating things. Since your class's state is made up of the _table field, all you need to do is remove the item with the given key from that field.
The following example replaces your untyped Hashtable wit a strongly-typed Dictionary. I also chose to initialize the dictionary with a LINQ statement, but you could keep your old code there if you prefer.
public class QueryString
{
private readonly Dictionary<string, string> _table;
public QueryString(string querystring)
{
if (querystring.Length > 0)
{
var pairs =
from pair in querystring.Split('&')
let item = pair.Split('=')
select new {key = item[0], value = item[1]};
_table = pairs.ToDictionary(p => p.key, p => p.value);
}
}
public void Remove(string key)
{
_table.Remove(key);
}
}
You cannot assign a value to this since it is a reference to the object itself.
However, if you remove the line this = _table; , isn't things working as they should then? I guess your ToString() is somewhat using the hashtable to generate a "printer friendly" QueryString, and if that is the case, the way I see it, your Remove() method should be working (since you are replacing the _table variable with a new HashTable not including the key-value pair you want to exclude).
you are passing a querystring into the class so the original querystring IS intact.
However you then break down the querystring into a a Hashtable of key/value pairs. If you want to keep THAT intact you need to clone the HashTable and perform the remove on the clone.
In any case it's probably a good idea to keep the querystring you are passing in as a constructor parameter in a member variable for safe keeping.

Retrieve Arguments of a Workflow (with default values)?

Given is a Workflow Foundation 4 runtime that is working against a website ;)
We need to get the arguments of workflows to show the user an editor to enter the arguments. For that we need all arguments with names, types and - default values, as well as an indication whether an argument is required.
Workflows are stored as XAML files.
How to do that? The data seems to be in the Activity Metadata which seems to be not avaialble outside the Workflow. In addition, the Workflow Engine ModelService is for the Designer and has a lot of overhead.
Any easy way to retrieve this information?
I've already done something similar. Reflection might be your best (and only) option if you want a generic approach.
// Just an holder for InArgument informations
class InArgumentInfo
{
public string InArgumentName { get; set; }
public string InArgumentDescription { get; set; }
public bool InArgumentIsRequired { get; set; }
}
static ICollection<InArgumentInfo> GetInArgumentsInfos(Activity activity)
{
var properties = activity.GetType()
.GetProperties()
.Where(p => typeof(InArgument).IsAssignableFrom(p.PropertyType))
.ToList();
var argumentsCollection = new Collection<InArgumentInfo>();
foreach (var property in properties)
{
var descAttribute = property
.GetCustomAttributes(false)
.OfType<DescriptionAttribute>()
.FirstOrDefault();
string description = descAttribute != null && !string.IsNullOrEmpty(descAttribute.Description) ?
descAttribute.Description :
string.Empty;
bool isRequired = property
.GetCustomAttributes(false)
.OfType<RequiredArgumentAttribute>()
.Any();
argumentsCollection.Add(new InArgumentInfo
{
InArgumentName = property.Name,
InArgumentDescription = description,
InArgumentIsRequired = isRequired
});
}
return argumentsCollection;
}
This way you can not only retrieve the argument's name but also other information hold by the argument's attributes. For example I choose to give argument an user-friendly name through [Description] attribute (eg. instead of MyPropertyName user sees "My Property Name").
Note: if you can ensure that you activity is an ActivityBuilder or DynamicActivity they both have Properties property that you can use, but the principle is the same.
Load it as DynamicActivity and iterate over Properties property
var dynamicActivity = ActivityXamlServices.Load(foo) as DynamicActivity
foreach(DynamicActivityProperty prop in dynamicActivity.Properties)
{
// ...
}
UPDATE: Missed default value part
foreach (var prop in dynamicActivity .Properties)
{
object defaultValue;
if (prop.Value == null)
{
defaultValue = null;
}
else
{
Type genericTypeDefinition = prop.Type.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(InArgument<>) || genericTypeDefinition == typeof(InOutArgument<>))
{
var valueProp = prop.Value.GetType().GetProperty("Expression", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly);
var expression = valueProp.GetValue(prop.Value, null);
var expressionValueProp = expression.GetType().GetProperty("Value");
defaultValue = expressionValueProp.GetValue(expression, null);
}
}
}
Not totally guaranteed, there are some checks you have to do.

Pass property from controller to Model

I am trying to pass a variable from a method in my Controller to a method in a Model. Since the method in the Model takes one argument (which was designed earlier), I cannot pass my variable as an argument to the method in the Model. And also, the method in this Model is called by other controllers too, so if I change the argument, I have to change all the controllers too, which would be a tedious task.
What I have been trying so far is- I created one MyVariableClass and declared a property. Then I instantiated that class and set the property string to the variable that I wanted to pass. Now, in my Model's method, I instantiated the same MyVariableClass again, but when I did that, the value of the variable was set to null. The code I have right now is -
public ActionResult ItemInformation( string id)
{
//Pass a string to MyVariable
MyVariableVClass params = new MyVariableClass();
params.myVariable = "abc";
//This is what My Model is taking as an argument(id), and I don't want to
//pass mYvariable along with that argument because it will break other controllers
// too which calls this method
var itemInformation = _repository.GetItemInformation(id);
return View(itemInformation);
}
and MyVariableClass
public class MyVariableClass
{
public string myVariable { get; set; }
}
and the method in My Model
public IList<Items> GetItemInformation(string itemId)
{
MyVariableClass webType = new MyVariableClass();
var _params = webType.myVariable;
//Check this variable and perform database query
if (_params =="this")
{
var query = myFirstQuery;
}
else
{
var query = mySecondQuery;
}
//return ....
}
Anybody has solution to this? Thanks in Advance!
Any reason why subclassing your model and overriding the GetItemInformation method wouldn't work? Or, even easier, why not just overload the GetItemInformation method with one that takes two strings? Your other controllers can still use the one that only takes a single string.
public IList<Items> GetItemInformation(string itemId, MyVariableClass webType)
{
var _params = webType.myVariable;
//Check this variable and perform database query
if (_params == "this")
{
var query = myFirstQuery;
}
else
{
var query = mySecondQuery;
}
//return ....
}
public IList<Items> GetItemInformation(string itemId)
{
MyVariableClass fauxType = new MyVariableClass();
fauxType.myVariable = "not this";
return GetItemInformation(itemId, fauxType);
}
Try using session variable.

Resources