I have a working API with a bunch of controllers, with a single database specified in config file.
Now I want to make the the API multi database and make the target database a part of the url.
I use attributes on controllers now and default routing.
Startup.cs:
app.UseMVC();
FolderController.cs:
[Route("api/[controller]")]
[ApiController]
public class FoldersController : ControllerBase { ...
and action on controller:
[HttpGet("{Parent:Guid}", Name = "Get")]
public IActionResult Get(Guid Parent) {...
So what that gives me is the standard overall template that looks like this:
https://api.example.com/api/{controller}/{action}
What I'd want is to make the database a part of the url, the intuitive place being in front of the controller. I can also skip the second api bit as I'm not running anything else on that base address.
https://api.example.com/{database}/{controller}/{action}
I've been able to extract the database name by changing the controller attribute to:
[Route("{database}/[controller]")]
But then I'd have to insert code in every action method to check for route etc, with the risk of not implementing it consitently (beside the extra typing).
Ideally I'd like to add this to the default route in startup.cs, and add a service to the middleware that would check the privileges for the authenticated user on the requested database and continue as appropriate. That way I'd have my security in one place and no way to forget it in a controller.
I havent been able to figure out how to mix that with the attributes, they seem to conflict with each other.
Can this be done? Does anyone have some pointers for me get out of this?
By understand I know we can do it. You need to implement IHttpHandler.
You can refer to the following example https://www.c-sharpcorner.com/article/dynamic-and-friendly-url-using-mvc/
Related
I have an ASP.net Core 6 web application where I have two controller actions similar to this:
[Route("auth")]
[ApiController]
public class AuthController: ControllerBase {
[HttpPost]
[Route("login/credentials")]
public async Task<IActionResult> LoginWithCredentials(...)
{
//...
}
[HttpPost]
[Route("login/token")]
public async Task<IActionResult> LoginWithToken(...)
{
//...
}
}
What I would like to do is having any requests that is made towards:
/auth/login
to actually be forwarded to either LoginWithCredentials() or LoginWithToken() depending on a server-side configuration.
In other words, I would like to dynamically chose the controller action for any request that matches a certain route.
Is this possible? If so, how can I do this?
NOTE: I've tried using a DynamicRouteValueTransformer but it doesn't seem to do what I want, it can change the values of route parameters but not change the controller action that is invoked (either that or I'm not using it correctly, please enlighten me if that's the case)
ADDITIONAL NOTE: I see people asking why I don't just use a single action with an if statement inside. The reason is that one of the two actions has a custom TypeFilterAttribute that I've omitted from the example for brevity. Thus, I can't just merge the two into one because then either the one that needs the attribute would not have it or vice-versa the one that doesn't need it would have it. And I can't change the source code of this custom filter either to "neuter" it when it is not needed, because it comes from a library that I'm not authorized to modify.
I have the following controller which I wanted to use as an Web API Controller for ajax posts to retrieve data from my user table.
namespace MyProjectName.Controllers.API
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly myContext _context;
public UsersController(myContext context)
{
_context = context;
}
[HttpGet]
public List<string> GetInstitutionNamesById(int id)
{
// returns desired list
}
}
}
Now I'd expect the routing of this Function to be like this: /api/users/getinstitutionnamesbyid but apparently it seems to be just /api/users which I find really confusing (what if I add additional HttpGet Functions?).
Can anyone explain me what I am doing wrong? Am I using Web Api Controllers not the Intended way? Is my routing wrong?
Thanks in Advance.
[Route("api/[controller]")]
With this template, you're explicitly stating that you only care about the name of the controller. In your example, GetInstitutionNamesById is the name of the action, which isn't being considered by the template.
There are a few options for achieving what you're asking for here:
Change your [Route] template to include the action name:
[Route("api/[controller]/[action]")]
This option applies to all actions within your controller.
Change the HttpGet constraint attribute to specify the action implicitly:
[HttpGet("[action]")]
This option ensures that the name of your action method will always be used as the route
segment.
Change the HttpGet constraint attribute to specify the action explicitly:
[HttpGet("GetInstitutionNamesById")]
This option allows you to use a route segment that differs from the name of the action method itself.
In terms of whether you're using routing in the correct way here - that's somewhat opinion-based. Generally, you'll see that APIs are attempting to be RESTful, using route templates that match resources, etc. With this approach, you might have something more like the following:
/api/Users/{userId}/InstitutionNames
In this case, you might have a separate InstitutionNames controller or you might bundle it up into the Users controller. There really are many ways to do this, but I won't go into any more on that here as it's a little off-topic and opinion-based.
You just need to name it this way
[HttpGet("[action]/{id}")]
public List<string> GetInstitutionNamesById(int id)
{
// returns desired list
}
and from ajax call /api/users/GetInstitutionNamesById/1
I was just assigned to implement one functionality in project that uses Umbraco. My job is to basically generate specific XML and return it to user. However i cannot get it to work, because when i create new controller (i've tried creating
Controller, RenderMvcController and SurfaceController
) and method in it (also if i just create new method in existing controller), i get error 404 after typing url into browser. Example: I create TestController and method Index in it. I've tried combinations where TestController was derived from RenderMvcController or SurfaceController or just Controller. After compiling, etc. when i run
http://my_address/Test
or
http://my_address/Test/Index
i get 404 error from umbraco. I looked at another pages in umbraco that were already in project and they all are also configured somehow in umbraco web panel:
http://my_address/umbraco
I aslo tried adding new methods to existings controllers, but no luck (again 404 errors). I've never worked with umbraco and i don't know how to configure it. I just want to know if there is any way to create method which will be accessible at:
http://my_address/MyMethod
or
http://my_address/MyController/MyMethod
and would return just exactly what i will program it to (without any Views, Partial Views, etc. - i can set Headers and ContentType manually and my content is pure text) in an existing Umbraco project without having to deal with umbraco admin panel?
Thanks for any help :)
//Edit
My mind is officially blown... My response is culture dependent (i mean i pull different data from db depending on country), but it's not as simple as
CurrentCulture.CultureInfo
Umbraco is configured to return different culture based on domain extension (Germany for .de, Great Britain for .co.uk, and Dennmark for .dk - it's just a manual configuration in umbraco admin panel assigning different culture info and views to different hostnames). Regular controllers get this modified culture from
RenderModel.CurrentCulture
passed as argument to controller's method. Is there a way to create umbraco controller/method/anthing that will not have layout/model assigned to it (so i can display pure XML data i receive from external service) and still have access to umbraco's RenderModel's culture? What i am trying to create is if user types url:
http://my_address.de/myController/myMethod
my controller will get current culture, call external service passing culture as parameter and display received data without wrapping it in any views. Example:
public class myController : SomeBaseUmbracoControllerOrsomething
{
public string/XmlDocument/ActionResult myMethod(RenderModel model)
{
int countryId = myFunctionToTranslateCultureToCountryId(model.CurrentCulture);
return MethodThatCallsExternalServiceAndReturnsXml(countryId);
}
}
Sorry for confusion, but i've learned about this whole mess with countries just now...
You don't want to use
controller, because this is not picked up by umbraco routing process
you don't want to use RenderMvcController, because this is overkill
you don't want to use Surfacecontroller because you are not using a Child action or form.
What you need is a UmbracoApiController (http://our.umbraco.org/documentation/Reference/WebApi/) or is your umbraco version is PRE 6.1 then use /Base extention (http://our.umbraco.org/documentation/Reference/Api/Base/Index)
Or if you really want to skip ALL umbraco magic for a certain route, add the path to the web.config/AppSettings/umbracoReservedUrls.
I want to log visits only for some controllers (or routes) as it was possible with classic ASP.NET pages by checking/unchecking the 'log visits' checkbox in IIS.
Does anyone know if this is possible somehow? A solution without a custom logging component would be fantastic! Please share your knowledge, if you know how ;)
Thanks in advance
Create a BaseController which the controllers that you want to record data for inherit from. Then create an ActionFilter which overrides the OnActionExecuted method and apply it to the base controller. Something like this..
public class ActionExecutedFilter : System.Web.Mvc.ActionFilterAttribute
{
UnitOfWork unitOfWork= new UnitOfWork();
public override void OnActionExecuted(ActionExecutedContext filter)
{
Transaction tran = new Transaction();
tran.Controller = filter.ActionDescriptor.ControllerDescriptor.ControllerName;
tran.ActionName = filter.ActionDescriptor.ActionName;
tran.User = HttpContext.Current.User.Identity.Name;
tran.Date = DateTime.Now;
unitOfWork.TransactionRepository.Insert(tran);
unitOfWork.Save();
}
}
This will save to a database table called Transactions information for every time a action method is called on that controller, recording the user, controller and action method name. Obviously I just typed in the UnitOfWork method of saving to the database, you can just plug in whichever method you like. I usually keep these methods in a filters folder, add a using statement then add it to the controller like so;
[ActionExecutedFilter]
public class BaseController : Controller
{
}
Again, just make all the controller you wish to record data from inherit the BaseController.
You could also use something like Log4Net, but I find just doing it this way gets what I need. Whatever you think yourself.
http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/
A solution without a custom logging component would be fantastic!
Apart from basic IIS diagnostic logs, I cannot think of any ASP.Net MVC specific Logging features in IIS.
One simple solution what I want to offer is to write a HttpModule. In the HttpModule you can log the requests. Also if you want to have control on what to log and what not to, then based on Routes you can make that happen in HttpModule. Advantage with HttpModule is it is easy to plug in and also easy to remove.
Make HttpModule work only on certain routes
When it comes to logging itself, you can have your own custom logic to database or to log file. But you can opt for ELMAH or Log4Net or Enterprise Logging Application Block
TL;DR Summary: Can I configure MVC Web API routing for HTTP GET, PUT & DELETE?
I've been looking into replacing our old Data Access Layer (a DLL based on DataSets and TableAdapters) with a private API, with a view to creating a public API if it's successful. I've done some work with MVC 4 to refresh our frontend, and loved working with it, so it seems sensible to explore the "Web API" project type before diving into WS- or WCF-based libraries.
An initial demo allows me to return XML/JSON nicely, for example:
//service.url/api/Users
... returns a list of users, while a specific user's details can be accessed via:
//service.url/api/Users/99
So far, so RESTful. However, in order to truly map URIs to resources I want to do an HTTP PUT (new user) or HTTP DELETE (remove user) to the the URI listed above. In all of the examples I've seen for these projects, along with the Scaffolds provided in Visual Studio, this convention is followed:
//service.url/api/Users/Create
//service.url/api/Users/Delete/99
//service.url/api/Users/Update/99
... and so on. This feels like side-stepping the issue to me, which is a shame when what's there has been put together so nicely!
Any thoughts on how best to approach this?
What you want is the default in MVC Web API. I'm not sure what you are looking at but here is a great example of routing the Get/Post/Put/Delete to actions.
For example you may want:
public class UsersController : ApiController
{
// GET http://service.url/api/Users/1
[HttpGet]
public User GetUser(int id);
// POST http://service.url/api/Users/?name=richard...
[HttpPost]
public User AddUser(User model);
// PUT http://service.url/api/Users/?id=1&name=Richard...
[HttpPut]
public User UpdateUser(User model);
// DELETE http://service.url/api/Users/1
[HttpDelete]
public User DeleteUser(int id);
}
I've explicitly set these, but the GetUser and DeleteUser don't need the prefix because they start with the matching HTTP method.
The link provided by Erik is a good start, but I see how it can confuse the situation when looking for a simple RESTful API that makes use of the HTTP verbs to perform these CRUD actions. If you're looking to use the HTTP verbs of GET, PUT, POST, and DELETE (and possibly PATCH, but I'm not covering that here) and you're ok with using convention, then the following would work:
public class UsersController : ApiController
{
// GET http://service.url/api/Users
public User GetAllUsers(){ ... }
// GET http://service.url/api/Users/1
public User GetUser(int id){ ... }
// POST http://service.url/api/Users/
// User model is passed in body of HTTP Request
public User PostUser([FromBody]User model){ ... }
// PUT http://service.url/api/Users/1
// User model is passed in body of HTTP Request
public User PutUser(int id, [FromBody]User model){ ... }
// DELETE http://service.url/api/Users/1
public User DeleteUser(int id){ ... }
}
Note that the attributes on the method are not needed when using the HTTP verb action convention in Web API. Also, note that I use the [FromBody] attribute on the User parameter for POST and PUT to denote that the body contains the data I wish to send. This may not be most convenient for POST if you're trying to append to a resource, and I have not tried creating/modifying data through query parameters using Web API. It certainly makes the call feel very clean to place your data in the body. "POST/PUT this content in the body at this resource."
Also, the way I read PUT in the spec, and I could very well be wrong, is that it acts as a replace. That also makes sense given the last line above. I'm PUTting this resource in this location, replacing what was already there. The spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) states: "If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server." The term they use is "modified" so I guess that leaves enough room for interpretation for the end user. That's where PATCH comes in (https://www.rfc-editor.org/rfc/rfc5789), but I don't have enough information to comment on that at this time.