I have a typical API with some CRUD operations. I typically need to get certain objects, based on different parameters.
One way to do it would be to have methods like:
GetProjectsByCustomerId(int customerId);
GetProjectsBySigneeId(int signeeId);
However, in my service layer (ProjectService in this case) I usually use a method such as the following where ProjectSpecification typically has quite a lot of fields and even lists:
public IEnumerable<Project> GetBySpecification(ProjectSpecification projectSpecification)
That means, in my dream world I would like to have endpoints such as:
/api/projects (empty specification, return full list)
/api/projects?customerid=2 (gets projects for customer with id 2)
/api/projects?signeeid=2,3 (get projects with signee id 2 and 3)
My question is - how is this done
My first attempt was adding this in my ProjectController (calling my ProjectService):
public class ProjectsController : ApiController
{
public IEnumerable<Project> GetProjects(ProjectSpecification projectSpecification)
{
var projects = _projectService.GetBySpecification(projectSpecification);
return projects;
}
}
But lets say I open this URL:
/api/Projects?CustomerId=2
This is not parsed into a ProjectSpecification viewmodel. However, if I change my controller signature to:
public IEnumerable<Project> GetProjects(int customerid) { }
It would work, because it's a simple type.
I could of course build some parameter-hell, but I guess there is something super obvious MVC magic I am missing - probably in the routing? :-)
Referencing documentation
Parameter Binding in ASP.NET Web API : [FromUri]
To force Web API to read a complex type from the URI, add the
[FromUri] attribute to the parameter.
For example assuming
public class ProjectSpecification {
public int CustomerId { get; set; }
//...other properties
}
public class ProjectsController : ApiController {
[HttpGet]
public IHttpActinoResult GetProjects([FromUri]ProjectSpecification projectSpecification) {
return Ok(projectSpecification);
}
}
The client can put the CustomerId value in the query string.
For example:
/api/Projects?CustomerId=2
and Web API will use them to construct a ProjectSpecification with the CustomerId set to 2 .
Related
I've got a pretty basic ASP.NET Core 2 Web App (no razor views, just MvcCore with json responses).
I'm trying to do a pretty simple GET request in Postman and my Controller Action isn't binding the query string parameters to my custom POCO.
here is a sample url which postman tries to hit: http://localhost:51459/orders?Query=iphone&MinimumPrice=22
public class OrderQuery
{
public string Query { get; set; }
public decimal? MinimumPrice { get; set; }
public decimal? MaximumPrice { get; set; }
}
[Route("orders")]
public class OrdersController : ControllerBase
{
[HttpGet("")]
public async Task<ActionResult> GetOrdersAsync(OrderQuery query)
{
// query.Query is null.
// all the properties of query are null.
}
}
Now I can step through the method (i.e. breakpoint is hit), so the route does get 'found' and 'handled'.
Secondly, I've also tried sprinkling [FromQuery] attributes on the properties in the POCO.
Lastly, I've tried changing the case in the request but I thought model biding is case insensitive.
Can anyone see what I'm doing wrong? Is there a particular middleware I should check to see if I've wired up/not wired up?
OMG # me :(
So the variable name in the method signature is query and the (string) querystring key is also query.
The model binding was getting confused with which query do I mean? The property of the OrderQuery class? Or trying to set the string to actual method variable, which it cannot do.
Solution: renamed the entire signature to: public async Task<ActionResult> GetOrdersAsync(OrderQuery orderQuery)
** Note the method signature variable name change **
Doh! :)
TL;DR; Don't name the POCO variable name to a form/querystring/route key.
May be this is a stupid question.
I have some model class in a Asp.Net web api 2.2 application, which implements an interface ICountryOfOrigin.
I need to filter records by applying where clause as shown below. I have to repeat this logic in many controllers with different models which implement ICountryOfOrigin.
Is it possible to move the filtering logic into a separate method and apply it to the controller action through data annotation?
My intention is to eliminate the repeating code.
Is it possible?
//Interface
public Interface ICountryOfOrigin
{
string Country {get;set;}
}
//Model
public class Product : ICountryOfOrigin
{
..
string Country {get;set;}
}
//Action
public IHttpActionResult Get()
{
List<string> euCountries = GetEuCountries();
Product product = _repository.Products.GetAll().Where(p=> euCountries.Contains(p.countries); // The filter is applied here
return Ok(products);
}
//Need to achieve something like this
[EuCountriesOnly]
public IHttpActionResult Get()
{
List<string> euCountries = GetEuCountries();
Product product = _repository.Products.GetAll();
return Ok(products);
}
Any experts help me on this?
I guess I would shoot anybody who would implemented it the way you want. Believe me, it's not cool at all to alter behavior of some method you're calling with an attribute you put on a calling method.
Just put the logic into an extension method on your repository type and call it a day:
public static class RepoExtensions
{
private static readonly euCountries = new Country[]{};
public static IEnumerable<ICountryOfOrigin> GetEU(this Repository repo)
{
return repo.Products.GetAll().Where(p=> euCountries.Contains(p.countries);
}
}
I'm assuming that your repository is of type Repository, you'll need to put the real type instead of it.
I'm in the process of designing a RESTful Web API and have bumped into the following problem: I need a controller to retrieve collections (called Sections) of a hierarchical structure as well as to retrieve a single part (a single Section). If I need a collection I have to refer to the ID of the root Section which gives me a subtree of the whole structure. So I went ahead and defined a SectionsController like this:
public class SectionsController : ApiController
{
// GET api/sections/5
// Gets a subtree.
public IEnumerable<Section> Get(int rootId)
{
...
}
// GET api/sections/5
// Gets a single section.
public Section Get(int sectionId)
{
...
}
Which obviously doesn't work as the signatures are identical. What is the recommended way to go about this?
If you want to follow standard REST patterns you should introduce a slightly different API:
public class SectionsController : ApiController
{
// GET api/section
public IEnumerable<Section> GetAll()
{
...
}
// GET api/section/5
public Section Get(int sectionId)
{
...
}
Normally you should use singular resources and provide identifier only for a specific one. You can't have same URLs, even with different controllers.
Reading this post on SO regarding image transfer and following the link provided I realized there is a very simple solution to this problem that respects REST and at the same time doesn't require additional controllers.
Just return a collection of the subtree IDs within the object requested for a particular ID, i.e.
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int[] DescendantIds { get; set; }
}
So with a single call to
api/section/5
I get all the details for the section with ID 5 as well as the IDs of the sections below. Yes, there's some overhead involved, so you have to decide for yourself if this solution is for you.
question about collection helper classes for view models with in a Web API <> View Models <> Entity Framework <> Database structure.
I'm trying to get a design started on a project which will use an "API" front end to get data, then HTML/Javascript pages to get and render.
In trying to create an MVVM approach to this I am not sure if what I want to implment is a good approch or not.
And that is a get Collection methods for the ViewModels. The user on the HTML side, will only be able to edit one Object at a time, so the collection is only to provide arrays/listing of the objects, making the Web API classes "less cluttered".
Using the Visual Studio MVC/Web API porject / c# (Visual Studio 2013 so MVC 5 components)
so using example of a Chair object
NOTE: in actual code would not do "new MyEntity().Chairs." all the time, it would be set as a variable with in the class. Simply written example long hand to make clear.
Web API part:
we will pluralize api objects front point
Web API controller = ChairsController
With 2 gets
namespace FurnitureAPI.Controllers
{
[Authorize]
public class ChairsController : ApiController
{
// GET api/chairs
public IHttpActionResult Get()
{
var chairs = ViewModels.Chairs.Get();
return Ok(chairs);
}
// GET api/chairs/5
//public string Get(int id)
public IHttpActionResult Get(int id)
{
var chair = ViewModels.Chairs.Get(id);
return Ok(chair);
}
public ... GET() {...} //all chairs
public ... GET(int id) {} //single chair where id
In the database side, we have Chair table. This will be singular for database.
Database already exists, so using Entity Framework and database first modelling we get in Entities
var furniture_db = new Models.FurnintureEntities();
and
Models.Chair
So in the middle want a View Model Chair.
namespace FurnitureAPI.ViewModels {
public class Chair {
public int ChairID { get; set; }
public string Name { get; set; }
public Chair() { }
public Chair(int chairid, string name) {
ChairID = chairid;
Name = name;
}
public Chair(Models.Chair db_chair) {
ChairID = db_chair.ChairID;
Name = db_chair.Name;
}
}
In my thinking, the Web API ChairsController I do not want to have any entity framework code.
but I want api to call something to get a List/Collection of Chair view models
So Im thinking, add Chairs as a class in the ViewModel namespace, which will be static.
so web api becomes
public IHttpActionResult Get()
{
var chairs = ViewModels.Chairs.Get();
return Ok(chairs);
}
and the View Model Collection Chairs then looks like
namespace FurnitureAPI.ViewModels {
public static class Chairs {
public static List<Chair> Get() {
List<Chair> chairs = (from s in new Models.FurnintureEntities().Chairs
select new ViewModel.Chair {
ChairID = s.ChairID,
Name = s.Name
}).ToList();
return chairs;
}
public static Chairs Get(int id) {
var chair = new FurnitureEntities().Chairs.SingleOrDefault(c => c.ChairID == id);
return chair != null ? new ViewModel.Chair(chair) : null;
}
}
So the question, is this Chairs collection wrapper okay/ideal/bad/run for the hills/other, or is there a different approach someone could point to?
I do not understand why struggling to find reference to on the internet, sure hope its not grumpy old age kicking in.
I find lots of WPF MVVM Entity Framework database first stuff, but I just want to make 'simple' Web API to expose the database to make HTML views with.
Maybe I do not need to bother with the View Model and simply serialize the Entity in the Web API methods?
Thanks.
Unfortunately I've been struggling with this too and it seems this subject doesn't get the love it should have.
Anyway, among the tools there are, you can count:
Breeze# (MIT)
TrackableEntitities (MIT)
I have the same method I call in six controllers. Right now I copy and paste between each of the controllers. All the controllers are in the same namespace. The method returns a bool based on the passed id. For example:
public bool CheckSubmission(int id =0)
{
Get Records from DB with criteria
If Record available return true
else return false
}
I have been away from C++ C# for awhile and can't find how to write these once. I know in Rails I can put the shared functions in ApplicationController. I have seen several Questions on SO about this but not a clear example, they are more along the lines read up on OOP. Any help would be appreciated as I get back into this.
Create a ControllerBase class that inherits from Controller, place this method in it.
Have your controllers inherit from your base controller - they will get this implementation to use.
public class ControllerBase : Controller
{
public bool CheckSubmission(int id = 0)
{
Get Records from DB with criteria
If Record available return true
else return false
}
}
public class SomethingController : ControllerBase
{
// Can use CheckSubmission in here
}