I am trying to set up an Asp.Net forms site with an API.
I have succeeded in adding in selective authentication, so that pages starting "\api\" do not get redirected, but instead challenge for basic authentication.
I am now trying to use MS Web Api 2 to do the API routing.
The idea is to be as RESTful as possible. I have a resource, a TradableItem, and initially I would want to allow API users to use HTTP GET in one of two ways.
If the API user passes no item key, the user receives a list of possible item keys
["ABC","DEF"...]
If the API user passes an item key as part of the URI, eg "/api/tradables/abc", a representation of the TradableItem is returned for the one with the key=ABC. (To my understanding, this is standard REST behaviour).
In Global.ASAX's Application_Start() function I have a route map like so...
RouteTable.Routes.MapHttpRoute(
name: "TradableItemVerbs",
routeTemplate: "api/tradables/{item}",
defaults: new { item = System.Web.Http.RouteParameter.Optional, controller = "Tradable" });
The TradableController.cs file looks like this...
public class TradableController : ApiController
{
private static CustomLog logger = new CustomLog("TradableController");
// GET api/<controller>
public IEnumerable<string> GetKeys()
{
var prefix = "GetKeys() - ";
string msg = "";
msg = "Function called, returning list of tradable pkeys...";
logger.Debug(prefix + msg);
// Get a list of tradable items
return TradableManager.GetTradablePkeys();
}
// GET api/<controller>/<pkey>
public string GetTradable(string pkey)
{
string msg = string.Format("Would get Tradable data for key: >{0}<", pkey);
return msg;
}
}
The problem is that only the GetKeys() function fires, whether I call GET to "/api/tradables" or "/api/tradables/abc".
For reference, using VS2015 Community, IIS 7.5, targeting .Net 4.6.1. I used Rick Strahl's blog as a guide on this (among other sources).
http://weblog.west-wind.com/posts/2012/Aug/21/An-Introduction-to-ASPNET-Web-API#HTTPVerbRouting
please, change the name of your param to item (because, this is the name define in the routes):
public string GetTradable(string item)
{
....
}
or when you call the method be explicit with the parameter name: /api/tradables?pkey=abc
Related
I would like to know how we will create a Route URL to access the above function. Error message comes stating that I cannot access the controller
[HttpGet]
[Route("api/TeacherData/ListTeachers/{SearchKey?}&{order?}")]
public List<Teacher> ListTeachers(string SearchKey = null, string order = null)
{
}
I know it's your API and your design, but following the REST API patterns we should stick to simple API URLs, something like api/teachers could be easier to understand by the consumers if they know that the endpoint uses the GET method.
About your actual question, you could change the code to use [FromQuery] to expect parameters that should come from the query string:
[HttpGet]
[Route("api/teachers")]
public List<Teacher> ListTeachers([FromQuery] string searchKey = null, [FromQuery] string order = null)
{
}
Then, from the consumer side, you could trigger this endpoint using the following URL:
GET http://myapi.com/api/teachers?searchKey=keyValue&order=orderValue
If you keep your URL structure it should something like this:
GET http://myapi.com/api/TeacherData/ListTeachers?searchKey=keyValue&order=orderValue
I've got a controller set up and working for all of my GET requests, but when it comes to the PUT requests my Web Site (not a Web App, if that makes any difference) is returning a 405.
I've got the route defined in my Global.asax Application_Start() with the other routes, and it's the first one, so it should be evaluated first (if my understanding is correct):
void Application_Start(object sender, EventArgs e)
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls
| System.Net.SecurityProtocolType.Tls11
| System.Net.SecurityProtocolType.Tls12;
RouteTable.Routes.MapHttpRoute("FilteredSpecialOrders",
routeTemplate: "api/sales/filteredRequests",
defaults: new { controller = "Sales", action = "filteredRequests" });
// subsequent routes are here
}
My SalesController has a method with the right attributes for type of request (Put) and the action name that matches my routeTemplate, as well as the [FromBody] attribute on the parameter:
[HttpPut, ActionName("filteredRequests")]
public IHttpActionResult PutSpecialOrders([FromBody] RequestFilter filter)
{
// do the needful
}
...and my client side code creates the body of the message (filter) as a JavaScript object and sends the .put requests via axios:
getFilteredRequests: async function () {
let filter = {
name: this.name,
age: this.yearsOld,
/* other name/value pairs of course */
};
const response = await axios.put(salesApi + 'filteredRequests', filter);
let data = response.data;
return data;
}
...but I'm always getting back a 405 - Method Not Allowed. What am I forgetting? I'm not sure how the JSON object that gets sent in the body is serialized into my RequestFilter object - does that happen automagically or do I need to define that somewhere? I've made sure that the names are the same on both ends, but other than that...
I edited my web.config to remove the WebDAV bits per this post - but I also had to switch my pipeline from classic mode to integrated to get it to work.
I Have a C# WinForms application and in a form I want to consume (Get) some data from an ASP Web API application.
in Web API I have a controller named Session inside this controller I have two methods as shown below :
public int Create()
{
Random random = new Random();
int New_ID = random.Next();
return New_ID ;
}
public string Get()
{
return "Catch this data";
}
So the problem is when I use browser URL (For testing) to access to controller (Session)
URL : http://localhost:52626/api/Session
I got
And when I want to access to controller (Session) especially Create Function
URL : http://localhost:52626/api/Session/Create
I got
The global question is how can I create my own methods and access to it without depending on Get ?
The browser always fires a GET. Web-API by default selects the method to be called using the HTTP verb (GET in your case) of request. Hence it always lands in "Get" method. To make Web-API point to your method you need to define a route. Use below:
[Route("api/session/Create")]
[HttpGet]
public int Create()
{
Random random = new Random();
int New_ID = random.Next();
return New_ID;
}
To better understand route based selection, read this : https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/routing-and-action-selection
I'm pretty new to C# and need to realise a REST Service so i stumbled over Grapevine.
I need to have parts of the URL of the service handed over on service start via config file but I don't manage to hand over the value "clientId" of the config file to the Route's Pathinfo because it's not constant.
Here's the part of the code:
[RestResource(BasePath = "/RestService/")]
public class Rest_Resource
{
public string clientId = ConfigurationManager.AppSettings["ClientId"];
[RestRoute(PathInfo = clientId + "/info")]//<-how do I fill Pathinfo with dynamic values?
public IHttpContext GetVersion(IHttpContext context)
{....}
}
I'm using grapevine v4.1.1 as nuget package in visual studio.
While it is possible to change attribute values at runtime, or even use dynamic attributes, an easier solution in this case might be to not use the auto discovery feature exclusively, but use a hybrid approach to route registration.
Consider the following class that contains two rest routes, but only one of them is decorated with the attribute:
[RestResource(BasePath = "/RestService/")]
public class MyRestResources
{
public IHttpContext ManuallyRegisterMe(IHttpContext context)
{
return context;
}
[RestRoute(PathInfo = "/autodiscover")]
public IHttpContext AutoDiscoverMe(IHttpContext context)
{
return context;
}
}
Since you want to register the first route using a value that is not known until runtime, we can manually register that route:
// Get the runtime value
var clientId = "someValue";
// Get the method info
var mi = typeof(MyRestResources).GetMethod("ManuallyRegisterMe");
// Create the route
var route = new Route(mi, $"/RestService/{clientId}");
// Register the route
server.Router.Register(route);
This takes care of manually registering our route that needs a runtime value, but we still want the other routes to be automatically discovered. Since the router will only autodiscover if the routing table is empty when the server starts, we'll have to tell the router when to scan the assemblies. You can do this either before or after you manually register the route:
server.Router.ScanAssemblies();
I have an Asp.net MVC3 application I want to be able to allow multiple/ different clients to access the same application but using different url's. I have already managed to configure the database to allow this. So hia's the main part i want to host my application in a domain say... www.myapplication.com then allow different client to access the same application using 1.www.clientOne.myapplication.com 2.www.clientTwo.myapplication.com.
How to do it? Please provide the code. Thanks.
Stack Exchange itself runs on a multi-tenant architecture!
http://weblogs.asp.net/zowens/archive/2010/06/16/multi-tenant-asp-net-mvc-views.aspx
This set of articles should get you what you need to set up your basic architecture. It has plenty of code and does a pretty good job at covering what you'd need. I don't think you're going to get the exact code that you need here to set up the entire core architecture of your multi tenan application, I'd advise using either this article above or ones like it.
To implement multi tenant application you have to do some tricks in
C:\Windows\System32\drivers\etc host file
and some coding tricks in App_Start folder at RouteConfig.Csfile by inheriting and implementing IRouteConstraint interface. Also need to see bindings settings in IIS server(for this you need to see the video tutorial provided in below link)
You can get a complete code here https://github.com/ashikcse20/ASP-MVC5-MULTI-TENANT-REPOSITORY with complete written tutorial.
The video tutorial is given here ASP .NET MVC 5 Multi Tenant Example With Basic Code (Single Database Per Tenant)
Code in RouteConfig.Cs at RegisterRoutes function
routes.MapRoute(
name: "Default", url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { TenantRouting = new RoutingConstraint() }
);
Inheriting and implementing IRouteConstraint Interface
public class RoutingConstraint : IRouteConstraint // It is main Class for Multi teanant
{
public bool Match(HttpContextBase httpContext, Route route, string getParameter, RouteValueDictionary values, RouteDirection routeDirection)
{
// Got htis code from http://blog.gaxion.com/2017/05/how-to-implement-multi-tenancy-with.html
var GetAddress = httpContext.Request.Headers["Host"].Split('.');
var tenant = GetAddress[0];
//Here you can apply your tricks and logic. Note for when you put it in public server then www.hamdunsoft.com , www.tenant1.hamdunsoft.com then you need to change a little bit in the conditions . Because a www. was added.
if (GetAddress.Length < 2) // See here for localhost:80 or localhost:9780 ohh also for hamdun soft execution will enter here . But for less than 2? will hamdunsoft.com enter here?
{
tenant = "This is the main domain";
Constant.DatabaseName = "TEST";
if (!values.ContainsKey("tenant"))
values.Add("tenant", tenant);
//return false;
// return true;
}
else if (GetAddress.Length == 2) // execution will enter here for hamdunsoft.com enter here but not for www.hamdunsoft.com
{
tenant = "This is the main domain";
Constant.DatabaseName = GetAddress[0];
if (!values.ContainsKey("tenant"))
values.Add("tenant", tenant);
//return false;
// return true;
}
else if (!values.ContainsKey("tenant")) // for tenant1.hamdunsoft.com execution will enter here
{
values.Add("tenant", tenant);
Constant.DatabaseName = GetAddress[1]+"."+ tenant;
}
return true;
}
}