MVC6 controllers for related entities - asp.net-core-mvc

I want to provide access to a legacy database to customers via a REST API using ASP.NET 5 / MVC6.
There seem to be many examples for writing controllers for single entities but what I don't find is some guideline on how to write controllers for related entities.
In my domain model there are f.e. two entity classes:
Device
MeasuringPoint
and each device can have 1..N measuring points.
I want my controllers to have only Get actions, that is, the API is read-only.
For the Device entity I would use
public class DevicesController : Controller
{
// GET: api/devices
[HttpGet]
public IEnumerable<Device> Get()
{
//retrieve all devices
}
...
}
The next step would usually be for a user to query all measuring points for a specific (selected) device.
How would I write that controller action?
What is the recommended routing and how would I set that up?
For the client side I found suggestions like:
get api/devices/{ID}/measuringPoints
but I have no idea how this is implemented.

You can use attribute routing to achieve what you want.
If you have enabled MVC in your Startup class using app.UseMvc(), then you already have support for routing via the RouteAttribute.
[Route("api/[controller]")]
public class DevicesController : Controller {
// GET: api/devices
[HttpGet]
[Route("")]
public IEnumerable<Device> Get() {...}
...
// GET: api/devices/5/measuringPoints
[HttpGet]
[Route("{id}/measuringPoints")]
public IEnumerable<MeasuringPoint> GetMeasuringPoint(int id) {...}
}

Related

Web Api Routing: Multiple controller types were found that match the URL for parameter VS constant paths

My issue is similar to Web Api Routing : Multiple controller types were found that match the URL but I want to keep them in separate controllers.
From the comments, 2 preexisting answers are good workarounds but do not solve the actual issue I'm trying to resolve.
The URLs I'm making up are similar to nested directories in a file system OR are very similar to Firebase URLs.
/BiggestSet/{BiggestSetCode}/Subset1/{Subset1Code}/SubsetOfSubset1/{SubsetOfSubset1}
... etc all the way down to where ever the tree stops. Think of it as a tree of data.
/Collection/{Instance}/Collection/{Instance}
The issue I have is that at the /Collection level I want to also provide specific collection level operations. Like Add and search and other collection specific Operations Collection/ProccessData
Collection Controller:
/Collection/Add
/Collection/ProcessDataOnTheColleciton
Instance Controller:
/Collection/{InstanceCode}
/Collection/{InstanceCode}/ProcessOnTheInstance
The problem I'm having is the Collection/ProcessData clashes with the instance Collection/{InstanceCode}
NOTE: 1 is an parameter and the other is a constant.
If you setup the controllers so that collection and Instance are in the same controller. the /{InstanceCode} doesn't clash with the /ProcessData
BUT
If you setup so the controllers are split into logical functions WebAPI gives the error Multiple controller types were found that match the URL.
Does anyone know how to modify attribute routing to somehow behave as if they are in the same controller OR to prioritize the constant over the parameter across controllers?
To keep two separate controllers and still have such routes you can use regular expression route constraints. This way you can specify for the instanceCode you accept everything except the actions from the other controller.
Here is a sample of how to configure routes like that:
public class CollectionController : ApiController
{
[HttpGet]
[Route("Collection/Add")]
public string Add()
{
return $"CollectionController = Collection/Add";
}
[HttpGet]
[Route("Collection/Process")]
public string Process()
{
return $"CollectionController = Collection/Process";
}
}
public class InstanceController : ApiController
{
[HttpGet]
[Route("Collection/{instanceCode:regex(^(?!Add$|Process$).*)}")]
public string Get(string instanceCode)
{
return $"InstanceController = Collection/{instanceCode}";
}
[HttpGet]
[Route("Collection/{instanceCode:regex(^(?!Add$|Process$).*)}/Process")]
public string Process(string instanceCode)
{
return $"InstanceController = Collection/{instanceCode}/Process";
}
}
Here is also a link to the post that explains the regular expression used in the sample.
An even better option would be if you have a specific format for the instanceCode and set the regular expression to accept only this specific format. Then you would not need to modify the regular expression for every new action added. I include also a link to the documentation for all available Route constraints. There you can see all the available options. For example if your instance code is a number you don't even need a regular expression you can just restrict with the int constraint like this [Route("Collection/{instanceCode:int}")].

How to Call a controller function in another Controller in Laravel 5

im using laravel 5.
I need to call a controller function but this should be done in another controller.
I dont know how to do this
public function examplefunction(){
//stuff
}
And i have a Route for this function, so at
public function otherfunctioninothercontroller(){
// I need examplefunction here
}
how Can i do this?
1) First way
use App\Http\Controllers\OtherController;
class TestController extends Controller
{
public function index()
{
//Calling a method that is from the OtherController
$result = (new OtherController)->method();
}
}
2) Second way
app('App\Http\Controllers\OtherController')->method();
Both way you can get another controller function.
If they are not in the same folder, place use namespace\to\ExampleClass; on top of your file, then you are able to instantiate your controller.
You can simply instantiate the controller and call the desired method as follows
FirstController.php:
namespace App\Http\Controllers;
class FirstController extends Controller {
public function examplefunction() {
// TODO: implement functionality
}
}
SecondController.php:
namespace App\Http\Controllers;
class SecondController extends Controller {
public function test() {
$object = new FirstController();
$object->examplefunction();
}
}
Now, after i've answered the question, i would like to add the following comment:
Controllers are classes, all rules that applies to normal classes can be applied to them
However, instantiating a controller directly inside another controller to call a desired method signifies a problem in your design for the following 2 reasons:
A controller cannot obtain an instance of another controller directly
Controller should contain as little business logic as possible, and if possible none
The closest possible solution to what you want (WITHOUT BREAKING MVC) is to make an HTTP request to the route that points to the desired method (using cURL, for example) and read the response as the returned data
But this still doesn't make much sense in this scenario because after all you're making an HTTP request from a method in a controller in your project on your server to a method in a controller in your project on your server, seems like unnecessary overhead, right ?
As i said earlier, a controller should contain as little business logic as possible because the logic should stay inside specialized classes (commonly known as Service Classes), and when a processing is requested the controller simply delegates the job of processing to the appropriate service class which does the processing and returns the results to the controller which in turn sends it back as a response
Now imagine if you've the following scenario:
We've got an application that consists of 3 functionalities:
A user can register an account from web application
There's a mobile application that talks to an API to register a user
There's an admin panel, which he can use to add new user
Obviously you need to create 3 controllers, but those controllers contains repeated logic, would you copy/paste the code everywhere ?
Why not encapsulate this logic inside a service class and call it from the controller when needed ?
Let's say I have Controller1 and Controller2. I want to call a function of Controller1 from inside a function placed in Controller2.
// Controller1.php
class Controller1 {
public static function f1()
{
}
}
And on the other controller:
// Controller2.php
use App\Http\Controllers\Controller1;
class Controller2 {
public function f2()
{
return Controller1::f1();
}
}
Points to be noted:
f1() is declared static
A call to a controller from inside another controller is a bad idea. There is no sense of meaning of controllers then. You should just redirect to web.php to save safe whole architecture like this:
class MyController {
public function aSwitchCaseFunction(Request $requestPrm){
...
//getting path string from request here
...
switch($myCase){
case CASE_1:
return redirect()->route('/a/route/path');
....
}
}
}

How to map different HTTP methods on the same url to different controllers?

I have my API for a small part of my application split over two controllers, because of (external) requirements on the casing of JSON data in the API (some requests should use camelCasing, while others should use PascalCasing).
Now, I have a url that I want to map with PascalCasing for GET, but camelCasing for PUT, so I tried the following:
[PascalCasing] // custom attribute, part of our code
// We configure all controllers that *don't* have this to use
// camelCasing
public class PascalCasedController : ApiController
{
[HttpGet]
[Route("url/to/my/resource/{id}")]
public IHttpActionResult(int id)
{
return Ok(GetResource(id));
}
}
public class CamelCasedController : ApiController
{
[HttpPut]
[Route("url/to/my/resource/{id}")]
public IHttpActionResult(int id, Resource resource)
{
SaveResource(id, resource);
return Ok();
}
}
The GET request works as expected, but if I try to PUT something there with Fiddler, I get the following error message:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
MyProject.PascalCaseController
MyProject.CamelCaseController
I realize this is probably because WebAPI maps routes to controllers first and actions next, but if HTTP methods are considered, there really isn't any ambiguity here. Is there any way that I can tell WebAPI how to do this, without having to have the methods in the same controller?
#Tomas - There's an interface "System.Web.Http.Dispatcher.IHttpControllerSelector" exposed in System.Web.Http assembly. You can use that interface and create your own HttpControllerSelector. You can then replace the DefaultControllerSelector with your custom controller selector in the HttpConfiguration during AreaRegistration.
httpConfig.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(services.GetHttpControllerSelector()));
In this custom controller selector you can write your own implementation of SelectController() method of IHttpControllerSelector in which you can call GetControllerMapping() method of IHttpControllerSelector. This will give you the list of all the controllers registered. For every controller you can check for the DeclaredMethods and check for the CustomAttributes for each of the DeclaredMethods. In your case it will be either HttpGetAttribute or HttpPutAttribute.
Check the Method type of the incoming HttpRequestMessage (GET/PUT) and compare it against the value of the CustomAttributes. If you find a match of the combination of incoming request URL and the the respective Http Verb then you take that HttpControllerDiscriptor and return it from the SelectController() method..
This will allow you to have same URL with different methods in two different controllers.

Action parameters & Doctrine entities in Symfony 2

We're running a project built on top of Zend Framework 1.x, and are considering moving to Symfony 2. We have a domain model mapped with Doctrine 2.
Our (custom built) base controller class extends Zend_Controller_Action to provide a very convenient feature, inspired from Flow3:
Let's say I have this controller:
class UserController extends BaseController
{
public function editAction(User $user)
{
// ...
}
}
If I load this URL:
/user/edit?user=123
The base controller will automatically load the User entity with identity 123, and pass it as a parameter to the editAction() method.
If the user parameter is omitted, or if no User with this identity exists, an exception is thrown.
Is there such an implementation for Symfony 2, or is it possible to implement it, and how?
The #ParamConverter annotation from SensioFrameworkExtraBundle does exactly that. If you're using the Symfony Standard distribution, you get it out of the box.

MVC3 Routing Issues - How to re-use a View for all Controller Methods?

I'm trying to implement a common controller in MVC3 to return various JSON feeds, example -
public class AjaxController : Controller
{
public ActionResult Feed1()
{
ViewBag.Json = LogicFacade.GetFeed1Json();
return View();
}
public ActionResult Feed2()
{
ViewBag.Json = LogicFacade.GetFeed2Json();
return View();
}
}
This class has 30+ methods in it, the problem is this requires implementing an IDENTICAL View for each of the Controller's methods (sigh) that writes out ViewBag.Json.
I'm assuming this is a routing issue but I'm struggling with that. The following didn't work -
Tried setting ViewBag.Json then using RedirectToAction() but that seems to reset ViewBag.Json.
Note JsonResult is not appropriate for my needs, I'm using a different JSON serialiser.
So the objective here is to maintain one View file but keep this class with seperate methods that are called by routing, and not a crappy switch statement implementation.
Any help appreciated.
Use the same view and just specify the name. You can store in the controller's view folder, if only used by one controller, or in the Shared view folder if used by more than one.
return View("SharedJsonView");
Another, perhaps better, solution would be to create your own result -- maybe deriving from JsonResult, maybe directly from ActionResult -- that creates the JSON response that you need. Look at the source code for JsonResult on http://www.codeplex.com/aspnet for ideas on how to do it.

Resources