I'm making an API in asp mvc 4.
I have 2 methods, GetAllBooks() and GetPopularBooks().
To get all books, I call localhost:xxxxx/api/books/, and I get the correct result.
How can I query the popular books? Both methods are parameterless and our info here is not really helpful.
All help is appreciated!
Although this could be achieved using "Routing by Action Name" http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api. I strongly suggest that GetPopularBooks() be treated as GetAllBooks but with a filter of popularity.
e.g.
public IEnumerable<Books> GetAllBooks([FromUri]bool? IsPopular = null)
{
if(IsPopular.HasValue)
{ //do something
//return filtered
}
//return all
}
Urls:
http://localhost/api/books
http://localhost/api/books?isPopular=true
Related
In ASP.Net Core you have multiple ways to generate an URL for controller action, the newest being tag helpers.
Using tag-helpers for GET-requests asp-route is used to specify route parameters. It is from what I understand not supported to use complex objects in route request. And sometimes a page could have many different links pointing to itself, possible with minor addition to the URL for each link.
To me it seems wrong that any modification to controller action signature requires changing all tag-helpers using that action. I.e. if one adds string query to controller, one must add query to model and add asp-route-query="#Model.Query" 20 different places spread across cshtml-files. Using this approach is setting the code up for future bugs.
Is there a more elegant way of handling this? For example some way of having a Request object? (I.e. request object from controller can be put into Model and fed back into action URL.)
In my other answer I found a way to provide request object through Model.
From the SO article #tseng provided I found a smaller solution. This one does not use a request object in Model, but retains all route parameters unless explicitly overridden. It won't allow you to specify route through an request object, which is most often not what you want anyway. But it solved problem in OP.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
This requires an extension method to convert query parameters into a dictionary.
public static Dictionary GetQueryParameters(this HttpContext context)
{
return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString());
}
There's a rationale here that I don't think you're getting. GET requests are intentionally simplistic. They are supposed to describe a specific resource. They do no have bodies, because you're not supposed to be passing complex data objects in the first place. That's not how the HTTP protocol is designed.
Additionally, query string params should generally be optional. If some bit of data is required in order to identify the resource, it should be part of the main URI (i.e. the path). As such, neglecting to add something like a query param, should simply result in the full data set being returned instead of some subset defined by the query. Or in the case of something like a search page, it generally will result in a form being presented to the user to collect the query. In other words, you action should account for that param being missing and handle that situation accordingly.
Long and short, no, there is no way "elegant" way to handle this, I suppose, but the reason for that is that there doesn't need to be. If you're designing your routes and actions correctly, it's generally not an issue.
To solve this I'd like to have a request object used as route parameters for anchor TagHelper. This means that all route links are defined in only one location, not throughout solution. Changes made to request object model automatically propagates to URL for <a asp-action>-tags.
The benefit of this is reducing number of places in the code we need to change when changing method signature for a controller action. We localize change to model and action only.
I thought writing a tag-helper for a custom asp-object-route could help. I looked into chaining Taghelpers so mine could run before AnchorTagHelper, but that does not work. Creating instance and nesting them requires me to hardcode all properties of ASP.Net Cores AnchorTagHelper, which may require maintenance in the future. Also considered using a custom method with UrlHelper to build URL, but then TagHelper would not work.
The solution I landed on is to use asp-all-route-data as suggested by #kirk-larkin along with an extension method for serializing to Dictionary. Any asp-all-route-* will override values in asp-all-route-data.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net Core can deserialize complex objects (including lists and child objects).
public IActionResult HelloWorld(HelloWorldRequest request) { }
In the request object (when used) would typically have only a few simple properties. But I thought it would be nice if it supported child objects as well. Serializing object into a Dictionary is usually done using reflection, which can be slow. I figured Newtonsoft.Json would be more optimized than writing simple reflection code myself, and found this implementation ready to go:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}
MVC 4
I have an action that is decorated with an action filter like this:
[ViewPermission(PermissionType.GlobalUser)]
public ActionResult General()
{
var permissionType = // trying to access the value passed to the filter ie. PermissionType.GlobalUser value
return View();
}
Is there a way to get the properties from the acation filter inside the action itself?
Thanks in advance.
So the quick answer is yes, you can do it doing something like this:
[ViewPermission(PermissionType.GlobalUser)]
public ActionResult General()
{
var type = GetType(this);
var method = type.GetMethod("General");
var attribute(typeof(ViewPermission));
var permissionType = attribute.PermissionType;
return View();
}
With that said, it is NOT a good idea. Doing reflection is slow. Very slow. You would see performance problems. If you really need to do this, then it is best to figure out a way to do it during initialization of the app, where performance is not as much of a concern.
Hope that helps.
I'm whondering, wheather there is an easy way to pupolate doctrine entities from request objects. I'm building a RESTful API with fos/rest-bundle, so I dont need forms.
Do you know a good way to do this, in a very easy and short way?
// POST /api/products
public function postProductsAction(Request $request)
{
$product = new Product();
}
In addition, I'm whondering wheather its possible to inject instances of entities directly in the controller with post requests.
// PUT /api/product/1
// I need this functionality for post requests too
public function putProductAction(Product $product)
{
return $product; // { "id" : "1", "name" : "foo" }
}
Greetings,
--marc
What you need is the most common goal of every REST API. And the best way to do this is to use a serializer, in addition to forms (even if you would prefere to not use forms).
I advise you to read for example this tutorial writen by William Durand. It explains every points very well and uses the JMSSerializerBundle to convert entities through the API.
I noticed in the protobuf-net changelog that IList<> was supported but I'm getting the "Cannot create an instance of an interface" exception. If I change to IEnumerable<> then life is good. Does this sound correct?
// Client call
public override IList<IApplicationWorkflow> Execute(IRoleManagement service)
{
IList<ApplicationWorkflowMessagePart> list = service.RetrieveWorkflows(roleNames);
IList<IApplicationWorkflow> workflows = new List<IApplicationWorkflow>(list.Count);
foreach (ApplicationWorkflowMessagePart a in list)
{
workflows.Add(new ApplicationWorkflowImpl(a));
}
return workflows;
}
// Service contract
[OperationContract, ProtoBehavior]
[ServiceKnownType(typeof (ServiceFault))]
[FaultContract(typeof (ServiceFault))]
IList<ApplicationWorkflowMessagePart> RetrieveWorkflows(string[] roleNames);
// Service implementation
public IList<ApplicationWorkflowMessagePart> RetrieveWorkflows(string[] roleNames)
{
IList<IApplicationWorkflow> workflows = manager.RetrieveApplicationWorkflows(roleNames);
IList<ApplicationWorkflowMessagePart> workflowParts = new List<ApplicationWorkflowMessagePart>();
if (workflows != null)
{
foreach (IApplicationWorkflow workflow in workflows)
{
workflowParts.Add(
ModelMediator.GetMessagePart<ApplicationWorkflowMessagePart, IApplicationWorkflow>(workflow));
}
}
return workflowParts;
}
Thanks,
Mike
Also, is there document site that has this and other answers? I hate to be asking newb questions. :)
Currently it will support IList<T> as a property, as long as it doesn't have to create it - i.e. allowing things like (attributes not shown for brevity):
class Order {
private IList<OrderLine> lines = new List<OrderLine>();
public IList<OrderLine> Lines {get {return lines;}}
}
I would have to check, but for similar reasons, I expect it would work with Merge, but not Deserialize (which is what the WCF hooks use). However, I can't think of a reason it couldn't default to List<T>... it just doesn't at the moment.
The simplest option is probably to stick with List<T> / T[] - but I can have a look if you want... but I'm in a "crunch" on a (work) project at the moment, so I can't lift the bonnet today.
Re "this and other answers"... there is a google group, but that is not just protobuf-net (protobuf-net is simply one of many "protocol buffers" implementations).
You are also free to log an issue on the project site. I do mean to collate the FAQs and add them to the wiki on the site - but time is not always my friend...
But hey! I'm here... ;-p
Updated: 09/02/2009 - Revised question, provided better examples, added bounty.
Hi,
I'm building a PHP application using the data mapper pattern between the database and the entities (domain objects). My question is:
What is the best way to encapsulate a commonly performed task?
For example, one common task is retrieving one or more site entities from the site mapper, and their associated (home) page entities from the page mapper. At present, I would do that like this:
$siteMapper = new Site_Mapper();
$site = $siteMapper->findByid(1);
$pageMapper = new Page_Mapper();
$site->addPage($pageMapper->findHome($site->getId()));
Now that's a fairly trivial example, but it gets more complicated in reality, as each site also has an associated locale, and the page actually has multiple revisions (although for the purposes of this task I'd only be interested in the most recent one).
I'm going to need to do this (get the site and associated home page, locale etc.) in multiple places within my application, and I cant think of the best way/place to encapsulate this task, so that I don't have to repeat it all over the place. Ideally I'd like to end up with something like this:
$someObject = new SomeClass();
$site = $someObject->someMethod(1); // or
$sites = $someObject->someOtherMethod();
Where the resulting site entities already have their associated entities created and ready for use.
The same problem occurs when saving these objects back. Say I have a site entity and associated home page entity, and they've both been modified, I have to do something like this:
$siteMapper->save($site);
$pageMapper->save($site->getHomePage());
Again, trivial, but this example is simplified. Duplication of code still applies.
In my mind it makes sense to have some sort of central object that could take care of:
Retrieving a site (or sites) and all nessessary associated entities
Creating new site entities with new associated entities
Taking a site (or sites) and saving it and all associated entities (if they've changed)
So back to my question, what should this object be?
The existing mapper object?
Something based on the repository pattern?*
Something based on the unit of work patten?*
Something else?
* I don't fully understand either of these, as you can probably guess.
Is there a standard way to approach this problem, and could someone provide a short description of how they'd implement it? I'm not looking for anyone to provide a fully working implementation, just the theory.
Thanks,
Jack
Using the repository/service pattern, your Repository classes would provide a simple CRUD interface for each of your entities, then the Service classes would be an additional layer that performs additional logic like attaching entity dependencies. The rest of your app then only utilizes the Services. Your example might look like this:
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
Then inside the SiteService class you would have something like this:
function getSiteById($id) {
$site = $siteRepository->getSiteById($id);
foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
{
$site->pages[] = $page;
}
return $site;
}
I don't know PHP that well so please excuse if there is something wrong syntactically.
[Edit: this entry attempts to address the fact that it is oftentimes easier to write custom code to directly deal with a situation than it is to try to fit the problem into a pattern.]
Patterns are nice in concept, but they don't always "map". After years of high end PHP development, we have settled on a very direct way of handling such matters. Consider this:
File: Site.php
class Site
{
public static function Select($ID)
{
//Ensure current user has access to ID
//Lookup and return data
}
public static function Insert($aData)
{
//Validate $aData
//In the event of errors, raise a ValidationError($ErrorList)
//Do whatever it is you are doing
//Return new ID
}
public static function Update($ID, $aData)
{
//Validate $aData
//In the event of errors, raise a ValidationError($ErrorList)
//Update necessary fields
}
Then, in order to call it (from anywhere), just run:
$aData = Site::Select(123);
Site::Update(123, array('FirstName' => 'New First Name'));
$ID = Site::Insert(array(...))
One thing to keep in mind about OO programming and PHP... PHP does not keep "state" between requests, so creating an object instance just to have it immediately destroyed does not often make sense.
I'd probably start by extracting the common task to a helper method somewhere, then waiting to see what the design calls for. It feels like it's too early to tell.
What would you name this method ? The name usually hints at where the method belongs.
class Page {
public $id, $title, $url;
public function __construct($id=false) {
$this->id = $id;
}
public function save() {
// ...
}
}
class Site {
public $id = '';
public $pages = array();
function __construct($id) {
$this->id = $id;
foreach ($this->getPages() as $page_id) {
$this->pages[] = new Page($page_id);
}
}
private function getPages() {
// ...
}
public function addPage($url) {
$page = ($this->pages[] = new Page());
$page->url = $url;
return $page;
}
public function save() {
foreach ($this->pages as $page) {
$page->save();
}
// ..
}
}
$site = new Site($id);
$page = $site->addPage('/');
$page->title = 'Home';
$site->save();
Make your Site object an Aggregate Root to encapsulate the complex association and ensure consistency.
Then create a SiteRepository that has the responsibility of retrieving the Site aggregate and populating its children (including all Pages).
You will not need a separate PageRepository (assuming that you don't make Page a separate Aggregate Root), and your SiteRepository should have the responsibility of retrieving the Page objects as well (in your case by using your existing Mappers).
So:
$siteRepository = new SiteRepository($myDbConfig);
$site = $siteRepository->findById(1); // will have Page children attached
And then the findById method would be responsible for also finding all Page children of the Site. This will have a similar structure to the answer CodeMonkey1 gave, however I believe you will benefit more by using the Aggregate and Repository patterns, rather than creating a specific Service for this task. Any other retrieval/querying/updating of the Site aggregate, including any of its child objects, would be done through the same SiteRepository.
Edit: Here's a short DDD Guide to help you with the terminology, although I'd really recommend reading Evans if you want the whole picture.