Check if Model is valid outside of Controller - c#

I have a helper class that is passed an array of values that is then passed to a new class from my Model. How do I verify that all the values given to this class are valid? In other words, how do I use the functionality of ModelState within a non-controller class.
From the controller:
public ActionResult PassData()
{
Customer customer = new Customer();
string[] data = Monkey.RetrieveData();
bool isvalid = ModelHelper.CreateCustomer(data, out customer);
}
From the helper:
public bool CreateCustomer(string[] data)
{
Customter outCustomer = new Customer();
//put the data in the outCustomer var
//??? Check that it's valid
}

You could use the data annotations validation outside of an ASP.NET context:
public bool CreateCustomer(string[] data, out Customer customer)
{
customer = new Customer();
// put the data in the customer var
var context = new ValidationContext(customer, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
return Validator.TryValidateObject(customer, context, results, true);
}

Don't use ModelState outside of a controller. I can't see what Monkey.RetrieveData() does but in general I would not pass a) plain data from the HTTPRequest and b) untyped data like string-arrays to your backend. Let the web-framework check the incomming data and instanciate typed classes to use in the backend. Note that checking for HTML injection (XSS scripting etc.) must be done manually if you apply your data manually.
Instead use model-binders etc. and pass typed data (eg. Customer class instances) to your backend. There is an older post from Scott Gu that shows this for MVC1: http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx
In your example let the model binding of MVC create your customer and apply the field values required (see link above how that pattern works). You then give your Customer instance to your backend where additional validation checks can be done based on your typed Customer instance (eg. manually or with data annotations).

Related

Passing data between pages Xamarin Shell navigation

I have a Xamarin forms app with a form split across multiple pages, I want to pass the object data to the next or previous page. I am navigating using the Xamarin Shell. What method or setup can I use to achieve this?
The options I am aware of and my perceived issues with them:
JSON string the object and pass it as a parameter.
This seems incorrect as the data is being converted back and forth.
Pass every property of the object as an individual parameter.
Massively long winded with many properties and inflexible to change.
Store the data to a SQLite database.
I would not want to store an incomplete record in the table and using the current SQLiteAsyncConnection, I don't believe I can have 2 tables created from the same class.
Yes,you can pass data using query property attributes .
Navigation data can be received by decorating the receiving class with a QueryPropertyAttribute for each query parameter.
For more, check:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation#process-navigation-data-using-query-property-attributes .
In addition,another method is to create a global varible in your app, them you can access this varible in your app.
For example:
1.create class MyVariables.csand add static variable for your model (e.g. MyViewModel ) :
public class MyVariables
{
public static MyViewModel myViewModel { get; set; } = new MyViewModel { Name = "test1" };
}
MyViewModel.cs
public class MyViewModel
{
public string Name { get; set; }
}
2.You can modify or access your variable in your app:
// modify the variable
MyVariables.myViewModel.Name = "test2022";
// access the variable
System.Diagnostics.Debug.WriteLine("the data is: " + MyVariables.myViewModel.Name);
Unfortunately, we could only pass simple data now. And this feature will be added in the future: https://github.com/xamarin/Xamarin.Forms/issues/6848 You can create multiple QueryPropertyAttribute to access different data: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation#pass-data Another approach is to convert the object to a JSON string like:
var jsonStr = JsonConvert.SerializeObject(model);
Then pass it when navigating.

Set model binding form field name at runtime in ASP .NET Core

Rather than hard-coding the expected form field names for a DTO, is it possible for them to be dynamic / determined at run time?
Background: I'm implementing a webhook which will be called with form-url-encoded data (the shape of the data the webhook will be invoked with is out of my control).
Currently the signature of my controller actions look something like below:
public async Task<IActionResult> PerformSomeAction([FromForm]SomeWebhookRequestDto request)
The DTO is for the most part has a bunch of properties like below:
[ModelBinder(Name = "some_property")]
[BindRequired]
public string SomeProperty { get; set; }
where the form-field name is known to be "some_property" in advance (will never change)
However for some properties, I'll want to determine the form field name at runtime:
[ModelBinder(Name = "field[xxx]")]
[BindRequired]
public DateTime? AnotherProperty { get; set; }
Note that xxx will be replaced by a number (will change according to information in the URL).
Note that I'd rather avoid implementing custom model binders if I can - it seems I should just be able to hook in a IValueProvider - I've had a go at doing that (added a IValueProviderFactory, registered at position 0) - but it seems that [FromForm] is greedy and so my IValueProvider(Factory) never gets a chance.
To clarify some points:
The requests all have the same intent (they're all a request asking my API to do a single specific thing)
The requests all have the same semantic shape (let's say there are 10 fields, all 10 must be populated with valid data for that field - dates where dates are supposed to go, strings where strings are supposed to go). The meaning of the field-values are consistent too.
For the fields with names that must be determined at runtime, the field name will be something like "field[132]" or "field[130]". The names of those fields will depend on information provided in the URL - my API will perform a look-up to determine what the final names should be.
There could be a very large number of these configurations so setting up separate endpoints for each is not feasible.
While the above is somewhat of a nightmare, short of refusing to work on the gig it is out of my control
You're breaking several rules of good API design and just simply design in general here.
First, the whole entire point of a DTO is accept data in one form so you can potentially manipulate it in another. In other words, if you have different data coming through in different requests there should be different DTOs for each type of data.
Second, the whole point of an API is that it's an application programming interface. Just as with an actual interface in programming, it defines a contract. The client must send data in a defined format or the server rejects it. Period. It is not the responsibility of an API to accept any willy-nilly data the client decides to send and attempt to do something with it; rather, it is the client's responsibility to adhere to the interface.
Third, if you do need to accept different kinds of data, then your API needs additional endpoints for that. Each endpoint should deal with one resource. A client should never submit multiple different kinds of resources to the same endpoint. Therefore, there should be no need for "dynamic" properties.
Finally, if the situation is simply that all the data is for the same resource type, but only some part of that data may be submitted with any given request, your DTO should still house all the potential properties. It is not required that all possible properties be supplied in the request; the modelbinder will fill what it can. Your action, then, should accept the HTTP method PATCH, which by very definition means you're dealing with only part of a particular resource.
Solved by removing the [FromForm] attribute and implementing IValueProvider + IValueProviderFactory.
internal class CustomFieldFormValueProvider : IValueProvider
{
private static readonly Regex AliasedFieldValueRegex = new Regex("(?<prefix>.*)(?<fieldNameAlias>\\%.*\\%)$");
private readonly KeyValuePair<string, string>[] _customFields;
private readonly IRequestCustomFieldResolver _resolver;
private readonly ILogger _logger;
public CustomFieldFormValueProvider(IRequestCustomFieldResolver resolver, KeyValuePair<string, string>[] customFields) {
_resolver = resolver;
_customFields = customFields;
_logger = Log.ForContext(typeof(CustomFieldFormValueProvider));
}
public bool ContainsPrefix(string prefix) {
return AliasedFieldValueRegex.IsMatch(prefix);
}
public ValueProviderResult GetValue(string key) {
var match = AliasedFieldValueRegex.Match(key);
if (match.Success) {
var prefix = match.Groups["prefix"].Value;
var fieldNameAlias = match.Groups["fieldNameAlias"].Value;
// Unfortunately, IValueProvider::GetValue does not have an async variant :(
var customFieldNumber = Task.Run(() => _resolver.Resolve(fieldNameAlias)).Result;
var convertedKey = ConvertKey(prefix, customFieldNumber);
string customFieldValue = null;
try {
customFieldValue = _customFields.Single(pair => pair.Key.Equals(convertedKey, StringComparison.OrdinalIgnoreCase)).Value;
} catch (InvalidOperationException) {
_logger.Warning("Could not find a value for '{FieldNameAlias}' - (custom field #{CustomFieldNumber} - assuming null", fieldNameAlias, customFieldNumber);
}
return new ValueProviderResult(new StringValues(customFieldValue));
}
return ValueProviderResult.None;
}
private string ConvertKey(string prefix, int customFieldNumber) {
var path = prefix.Split('.')
.Where(part => !string.IsNullOrWhiteSpace(part))
.Concat(new[] {
"fields",
customFieldNumber.ToString()
})
.ToArray();
return path[0] + string.Join("", path.Skip(1).Select(part => $"[{part}]"));
}
}
public class CustomFieldFormValueProviderFactory : IValueProviderFactory
{
private static readonly Regex
CustomFieldRegex = new Regex(".*[\\[]]?fields[\\]]?[\\[]([0-9]+)[\\]]$");
public Task CreateValueProviderAsync(ValueProviderFactoryContext context) {
// Get the key/value pairs from the form which look like our custom fields
var customFields = context.ActionContext.HttpContext.Request.Form.Where(pair => CustomFieldRegex.IsMatch(pair.Key))
.Select(pair => new KeyValuePair<string, string>(pair.Key, pair.Value.First()))
.ToArray();
// Pull out the service we need
if (!(context.ActionContext.HttpContext.RequestServices.GetService(typeof(IRequestCustomFieldResolver)) is IRequestCustomFieldResolver resolver)) {
throw new InvalidOperationException($"No service of type {typeof(IRequestCustomFieldResolver).Name} available");
}
context.ValueProviders.Insert(0, new CustomFieldFormValueProvider(resolver, customFields));
return Task.CompletedTask;
}
}

How do I create a new object using dependency injection

At the moment I have a form where users create a new object. This is then passed to the controller as JSON.
How can I create a new object from this JSON to insert into the DB that I have, without doing
var x = new CustomObject {
ExampleField = JSONProperty,
ExampleField2 = JSONProperty2
};
repo.Create(x);
In general, you need something like this:
[HttPost]
public ActionResult CreateCustomer(string json)
{
var customer = JsonConvert.DeserializeObject<Customer>(json);
repo.Create(customer);
return View(customer);
}
An action method that takes as a parameter your json.
Use the JsonConvert.DeserializeObject method (I have supposed that you use
the Newtonsoft.Json library, the most used JSON framework for .NET.
The customer hew is an hypothetical object. You can follow the same approach for any custom object.
Last but not least this method return a View. This is optional, you can define another return type and return whatever you want.

Different Web API 2 controllers when using attribute routing

I'm building a prototype for a RESTful API using ASP.NET Web API 2. Fo simplictiy let's assume I have three entities: customers, licences and users. Each customer has a set of licences and users. Semantically it seems to me the resource URIs should look like this:
myurl/api/customers for accessing all customers
myurl/api/customers/{cid} for accessing the customer {cid}
myurl/api/customers/{cid}/licences for accessing all licences of customer {cid}
myurl/api/customers/{cid}/licences/{lid} for accessing the licence {lid} of customer {cid}
The same goes for the users. The intended semantics allow for example two users to have the same id if they belong to separate customers. Apart from maybe the licences entities (decision not final yet) each customer will have a dedicated database, so there is no overlapping in this domain and resource paths like
myurl/api/users
make only sense in the way "join all user tables from all customers' databases.
Using attribute routing this setup is quite easily achieved. However, all methods have to be implemented in the same controller since methods from different controllers cannot share the same prefix AFAIK.
The actual application will contain many more entities than just three, so I expect the controller's implementation to get quite huge. My question now is, how can I split the method into different controllers? I thought about using one main controller which just dispatches the work to be done to another controller. For example
[Route("{customer:int}/licences/{licence:int}")]
public HttpResponseMessage GetLicence(int customer, int licence)
{
// pretend the called method is static
return LicenceController.GetLicence(customer, licence);
}
However, I do not know how to implement this properly: Should I create a new LicenceController for each call? Or have a property of this type and call it's method? Actually implement some static methods?
Another downside is that this introduces hard-coded dependencies between the selector and the implementing controller classes which I feel is not a clean solution.
I came up with a workaround which uses resource paths like this:
myurl/api/licences/customer-{cid} for accessing all licences of customer {cid}
myurl/api/licences/customer-{cid}/{lid} for accessing the licence {lid} of customer {cid}
This works quite well but messes up the homogeneous semantics IMO. I know I can write a custom selector class but that seems to be quite some work to get it right.
So my question is, what is the best (perhaps most efficient) way to split the code which deals with incoming HTTP messages into separate controllers so that there is loose coupling and the resource semantics are coherent?
You would have two controllers. One to return the customers and one to return the licences. For the Customer there is no need to use attributes as the defaults are fine:
public class CustomersController : ApiController
{
// GET: api/Customers
public IEnumerable<Customer> Get()
{
return new List<Customer>
{
new Customer { Id = 1, Name = "Wayne" },
new Customer { Id = 2, Name = "John" }
};
}
// GET: api/Customers/5
public Customer Get(int id)
{
return new Customer { Id = 1, Name = "Wayne" };
}
}
Then you can you RoutePrefix attribute on the controller to add for api/Customers/1/licences and the rest can be handled by Route on the actions. I named the Controller CustomerLicencesController as you probably want to have a Licences controller to fetch a particular licence or all licences such as api/licences or api/licences/1.
[RoutePrefix("api/customers/{customer}/licences")]
public class CustomerLicencesController : ApiController
{
// GET: api/Customers/1/licences
[Route("")]
public IEnumerable<Licence> Get(int customer)
{
return new List<Licence>
{
new Licence { Id = 1, Name = "Test" },
new Licence { Id = 2, Name = "Test2" }
};
}
// GET: api/Customers/1/licences/1
[Route("{id}")]
public Licence Get(int customer, int id)
{
return new Licence { Id = 1, Name = "Test" };
}
}
For more information about Route attributes take a look at this.

View Models with database

I am having trouble understanding and implementing a view model. For example, say I have a Blog object, where each Blog object represents one blog post. I have a view that contains a list of each blog (title, text, date posted, etc...). Currently I am passing a list of blog objects to the view, but I would rather pass a list of BlogViewModel objects to the view. How do I do it? Does anyone have any good resources that will help me understand View Models?
Edit
The BlogViewModel I want to pass will contain abbreviated fields for the title and the text of the Blog. For example, I only want to show the first 10 characters of the title and the first 25 characters of the text.
Assuming you are currently doing something like:
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs);
}
To use view models you need to either return them from your service, or convert the objects in the controller before sending them on to the view.
One option is to create an extension method for the Blog object.
Say we have a few properties something like:
public class BlogVM
{
public string Title {get;set;}
public string Body {get;set;}
public string AuthorName {get;set;}
public int Id {get;set;}
}
We could write an extension method:
public static BlogVM ToBlogVM(this Blog source)
{
return new BlogVM
{
Title = source.Title.SubString(0, 10),
Body = source.Body.SubString(0, 25),
AuthorName = source.Author.Name,//assuming you have some kind of Author table, I'm sure you get the idea..
Id = source.Id
};
}
Now in your controller you can do something like
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs.Select(x => x.ToBlogVM()));
}
Which passes a list of BlogVM objects to your view.
edit: probably worth adding a few words on why ViewModels.
why send the view everything if it doesn't need it? In your example, your body might be a big block of text. If you are only going to display 25 chars, only send it 25 chars
some of the info in the object might be sensitive. You might want to send the Author's name, but certainly not other information you might hold such as his name, email or even password or address.
similarly, in a POST scenario you can control what information can potentially be sent back to you. If you allow the user to POST back to a full object, they can potentially send you back updated fields you might not expect. If you use a VM, you can control what information you will accept.
I find it easier/quicker for building views

Categories

Resources