what is the best way to consume web api in mvc - asp.net

I am using web api in my mvc application. I have method in web api which returns user detail using userId (which is in session["userID"])
public object getUserDetail()
{
//here is need of session["userID"]
// return somthing
}
so what is best way to access this web api method from jquery . Should i access this directly or first i should call my controller method and from there i should call this web api method.

You can directly call WebApi from jquery for performing operations(like insert/update/delete)other than returning JSON for processing back. For the scenarios where you require manipulating your view, call mvc controller which calls the Webapi.
So, for your case, the getUserDetail() method returns data. If these return values needs to be used in your view, then call it from mvc controller

WebApi is already an exposed endpoint for you to access your data from. Going to your controller, and calling the method from there diminishes the intent of having exposed the method as an Api in the first place. Try making a call to the route of the Api method, and you should be fine.
On a side note, try exposing a strongly typed object instead of just returning an object.

what is best way to access this web api method from jquery
Simply make an ajax call.
var url = www.example.com/api/user;
$.ajax({
type: 'GET',
url: url,
success: function(userValue) {
// Do something with your user info...
},
error: function(error) {
// Something went wrong. Handle error.
}
});
And have your controller return the value.
public class UserController : ApiController
{
[HttpGet] // For clarity only
public object Get()
{
// return your object.
return session["userID"];
}
}
And to get your url for the controller, you can use this in your view.
Url.HttpRouteUrl("DefaultApi", new {controller = "UserController "})})
Where DefaultApi is the route name defined in your route table (usually in RouteConfig.cs).
Edit:
Regarding access to session there's a number of ways to get around it. Take a look at this question and I think you will solve it. Accessing Session Using ASP.NET Web API
Or this tutorial:
http://www.codeproject.com/Tips/513522/Providing-session-state-in-ASP-NET-WebAPI
public class SessionableControllerHandler : HttpControllerHandler, IRequiresSessionState
{
public SessionableControllerHandler(RouteData routeData)
: base(routeData)
{}
}
public class SessionStateRouteHandler : IRouteHandler
{
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return new SessionableControllerHandler(requestContext.RouteData);
}
}
And lastly register it with your route:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
).RouteHandler = new SessionStateRouteHandler();
Or add this to your Global.asax.cs
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}

Feels like you might want to step back and rethink the basics. The main question here is: does it sound right that one view layer (MVC) calls another view layer (web api)? And simple answer is: no.
Usual setup is that your ajax calls target your Web Api controller methods directly. But if for whatever reason you find yourself thinking that you really need your MVC to call WebApi then that looks for extracting business logic to separate layer/tier so what you end up with is both, MVC and Web API, calling same method in separate class/layer (whatever your methods actually do).
So, instead of:
//this is in your MVC controller
public ActionResult SomeMVCAction(){
MyWebApiMethod();
}
//This is in your web api controller
public SomeStrongType MyWebApiMethod(){
var sum = 2+2;
}
you might want to have something like:
//this is in your MVC controller
public ActionResult SomeMVCAction(){
DoSum();
}
//This is in your web api controller
public SomeStrongType MyWebApiMethod(){
DoSum()
}
///This function is defined in separate layer/project which is your business layer
public static int DoSum(){
return 2+2;
}
PS.
Regarding session...There is a reason why session is not (easily) accessible in WebApi. REST Api should be stateless so you might want to rethink your design where you need session in web api controller.
You can describe a problem you're trying to solve by accessing session in web api controller and then we can try to give opinion on that.

Related

Understanding about constructor in Web Api

My web api looks like:
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ApplicationDbContext _context;
public ValuesController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet]
public string Get()
{
// get random user
var user = _context.Users.SingleOrDefault();
return user?.Email ?? "";
}
}
Trying to call it via jquery:
$.get('/api/values', function (email) {
console.log(email)
})
I've 2 questions:
1/. Why didn't console.log(email) work although I was getting the response successful?
There was nothing in the Console tab.
2/. When I made a request to server (/api/values), the breakpoint had been caught:
My question: where had ValuesController been called within context? I'd sent a request from client, then the constructor was hit (I'm sure that I didn't send something like ApplicationDbContext from client to server :v)
UPDATE: By changing $.get('/api/values', {}, function (email) {} to $.get('/api/values', function (email) {}. I've fixed the first problem. It's my bad. Sorry about that.
Shorter Answer
My question: where had ValuesController been called within context? I'd sent a request from client, then the constructor was hit.
The HTTP Request arrived. ASP.NET MVC Routing told the application to use the ValuesController to handle the request. The application constructed the ValuesController, supplying an instance of ApplicationDbContext via dependency injection.
Longer Answer
The Startup.Configure method is used to specify how the ASP.NET application will respond to individual HTTP requests. Since you are using Web API, you have configured app.UseMvc(). Result: when an HTTP Request arrives, MVC Routing tells the application to use the appropriate controller.
The Startup.ConfigureServices method is used to specify services that are available via dependency injection. Since you are injecting an ApplicationDbContext into your constructor, you have configured services.AddDbContext<ApplicationDbContext>(). Result: when the application constructs a ValuesController, ASP.NET Dependency Injection will provide an instance of the ApplicationDbContext.

In ASP.NET 5, how do I get the chosen route in middleware?

I am building an ASP.NET 5 (vNext) site that will host dynamic pages, static content, and a REST Web API. I have found examples of how to create middleware using the new ASP.NET way of doing things but I hit a snag.
I am trying write my own authentication middleware. I would like to create a custom attribute to attach to the controller actions (or whole controllers) that specifies that it requires authentication. Then during a request, in my middleware, I would like to cross reference the list of actions that require authentication with the action that applies to this current request. It is my understanding that I configure my middleware before the MVC middleware so that it is called first in the pipeline. I need to do this so the authentication is done before the request is handled by the MVC controller so that I can't prevent the controller from ever being called if necessary. But doesn't this also mean that the MVC router hasn't determined my route yet? It appears to me the determination of the route and the execution of that routes action happen at one step in the pipeline right?
If I want to be able to determine if a request matches a controller's action in a middleware pipeline step that happens before the request is handled by the controller, am I going to have to write my own url parser to figure that out? Is there some way to get at the routing data for the request before it is actually handled by the controller?
Edit: I'm beginning to think that the RouterMiddleware might be the answer I'm looking for. I'm assuming I can figure out how to have my router pick up the same routes that the standard MVC router is using (I use attribute routing) and have my router (really authenticator) mark the request as not handled when it succeeds authentication so that the default mvc router does the actual request handling. I really don't want to fully implement all of what the MVC middleware is doing. Working on trying to figure it out. RouterMiddleware kind of shows me what I need to do I think.
Edit 2: Here is a template for the middleware in ASP.NET 5
public class TokenAuthentication
{
private readonly RequestDelegate _next;
public TokenAuthentication(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//do stuff here
//let next thing in the pipeline go
await _next(context);
//do exit code
}
}
I ended up looking through the ASP.NET source code (because it is open source now!) and found that I could copy the UseMvc extension method from this class and swap out the default handler for my own.
public static class TokenAuthenticationExtensions
{
public static IApplicationBuilder UseTokenAuthentication(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
{
var routes = new RouteBuilder
{
DefaultHandler = new TokenRouteHandler(),
ServiceProvider = app.ApplicationServices
};
configureRoutes(routes);
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
routes.DefaultHandler,
app.ApplicationServices));
return app.UseRouter(routes.Build());
}
}
Then you create your own version of this class. In my case I don't actually want to invoke the actions. I will let the typical Mvc middleware do that. Since that is the case I gut all the related code and kept just what I needed to get the route data which is in actionDescriptor variable. I probably can remove the code dealing with backing up the route data since I dont think what I will be doing will affect the data, but I have kept it in the example. This is the skeleton of what I will start with based on the mvc route handler.
public class TokenRouteHandler : IRouter
{
private IActionSelector _actionSelector;
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
EnsureServices(context.Context);
context.IsBound = _actionSelector.HasValidAction(context);
return null;
}
public async Task RouteAsync(RouteContext context)
{
var services = context.HttpContext.RequestServices;
EnsureServices(context.HttpContext);
var actionDescriptor = await _actionSelector.SelectAsync(context);
if (actionDescriptor == null)
{
return;
}
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
if (actionDescriptor.RouteValueDefaults != null)
{
foreach (var kvp in actionDescriptor.RouteValueDefaults)
{
if (!newRouteData.Values.ContainsKey(kvp.Key))
{
newRouteData.Values.Add(kvp.Key, kvp.Value);
}
}
}
try
{
context.RouteData = newRouteData;
//Authentication code will go here <-----------
var authenticated = true;
if (!authenticated)
{
context.IsHandled = true;
}
}
finally
{
if (!context.IsHandled)
{
context.RouteData = oldRouteData;
}
}
}
private void EnsureServices(HttpContext context)
{
if (_actionSelector == null)
{
_actionSelector = context.RequestServices.GetRequiredService<IActionSelector>();
}
}
}
And finally, in the Startup.cs file's Configure method at the end of the pipeline I have it setup so that I use the same routing setup (I use attribute routing) for the both my token authentication and mvc router.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Other middleware delcartions here <----------------
Action<IRouteBuilder> routeBuilder = routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
};
app.UseTokenAuthentication(routeBuilder);
//Middleware after this point will be blocked if authentication fails by having the TokenRouteHandler setting context.IsHandled to true
app.UseMvc(routeBuilder);
}
Edit 1:
I should also note that at the moment I am not concerned about the extra time required to select the route twice which is what I think would happen here since both my middleware and the Mvc middleware will be doing that. If that becomes a performance problem then I will build the mvc and authentication in to one handler. That would be best idea performance-wise, but what I have shown here is the most modular approach I think.
Edit 2:
In the end to get the information I needed I had to cast the ActionDescriptor to a ControllerActionDescriptor. I am not sure what other types of actions you can have in ASP.NET but I am pretty sure all my action descriptors should be ControllerActionDescriptors. Maybe the old legacy Web Api stuff needs another type of ActionDescriptor.

MVC Ajax request without blocking main thread

i have some action inside controller likes:
public class ValuesController : Controller
{
[HttpPost]
public string GetInfo()
{
Thread.Sleep(30000); // logics imitation
return "result";
}
}
when I send request from client-side on this action I'll receive "Main Thread blocking" (like deadlock) while awaiting "logics imitations"
how i can prevent it?
already tried:
public class ValuesController : Controller
{
[HttpPost]
public async Task<string> GetInfo()
{
return await Task.Factory.StartNew(() =>
{
Thread.Sleep(30000);
return "result";
});;
}
}
Not working...
already looked (ASP.NET MVC and Ajax, concurrent requests?), but SessionState.ReadOnly way is not for me...
also tried using .svc service instead controller-action but have same troubles.
MVC by default blocks parallel sessions, and there is only one way to avoid it:
[SessionState(SessionStateBehavior.ReadOnly)]
Attribute on Controller and clearly separating logics with using writing and reading sessions.
This question was posted by my colleague. Yes, we have the situation where we need to "fire and forget" (we are trying to call actions asynchronically, when different actions are executed at the same time, but still all we have managed to get is to call actions one after another)

Front end ASP.NET MVC4 as one project, and an ASP.NET Web API as another project in same solution - how to call WebAPI from front end?

I have an ASP.NET MVC4 front end as one project in my solution, and a separate ASP.NET Web API as another project in the same solution. The Web API will contain all of my CRUD operations.
2 questions
How do I call my Web API from my front end to perform CRUD operations? I have my entity data model defined in my Web API project, and I will need to bind my front end views to it, how would I do that?
Once this is deployed to my web servers, the front end will reside on one server, and the Web API will reside on another server (the server that holds most of our web services). So, I guess along the same lines, how would I call the Web API from my front end once deployed? I understand Web API's are simply called with an HTTP request, but in terms of passing my models (which are defined in my Web API project) into my Views (in my front end project), how can I do this?
While Kevin is right, I did this the non-Ajax way. Keep in mind that I am working with JSON data, so this is centered around JSON.
In your controller page, remove anything that has to do with DbContext, Entity Framework, etc. The reason is by default, the controller will want to perform CRUD operations by calling the DbContext, and we don't want this. We want to call the WebAPI instead to do this.
First and foremost, declare some member variables in your controller. The rest of your controller will utilize these:
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
Uri contactUri = null;
In your controller, create a constructor for your controller, as such:
public ContactController()
{
// set base address of WebAPI depending on your current environment
client.BaseAddress = new Uri("http://server/YourAPI/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
Replace the Index action's code with something like the following. Note that the only relevant pieces are the client.GetAsync() call and the var contacts assignment. Everything else is not necessary for the context of this problem. The value inside the client.GetAsync() should be the name of your controller, prepended by any custom routing you set up in your WebApiConfig.cs - in my case, I added the api part in my route to distinguish between API calls and normal calls:
public ActionResult Index()
{
response = client.GetAsync("api/contact").Result;
if (response.IsSuccessStatusCode)
{
var contacts = response.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
return View(contacts);
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Create action (the HttpPost action) with something like the following. Again, the only important piece is the client.PostAsJsonAsync() part - this is what calls the WebAPI's POST action which takes care of, in my case, inserting a new record into the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Contact contact)
{
// Create a new product
response = client.PostAsJsonAsync("api/contact", contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Edit action (the non-HttpPost action) with something like the following. This was a little tricky because in order to edit, you had to retrieve the record first, so basically, the HttpPost version of Edit will contain somewhat similar code, with an additional line of code that performs the edit POST (PUT). Below, we're getting the response from the WebAPI by passing it a specific record ID. So, just like for Index (GET), we are doing the same thing only passing in the ID so we only get back one record. Then, we cast the response to an actual object that can be operated on in the View:
public ActionResult Edit(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Replace the Edit action (the HttpPost action) with something like the following. Below, we're getting the record to be edited by calling client.GetAsync() and passing in the primary key as a parameter (contact_id). Then, we're getting the RequestUri from that response and saving it. Then, we're calling client.PutAsJsonAsync() and passing in the Uri.PathAndQuery (what we just saved) as well as the object to be edited.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Contact contact)
{
response = client.GetAsync(string.Format("api/contact/{0}", contact.contact_id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.PutAsJsonAsync(contactUri.PathAndQuery, contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Delete action (the non-HttpPost action) with something like the following. So again, we're getting the record from the database by simply calling client.GetAsync() and casting it to an actual object my app knows of.
public ActionResult Delete(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Finally, replace the Delete action (the HttpPost action) with something like the following. Again, we're doing something similar to that of the Edit action. We are getting the record to be deleted, casting it to an object, and then passing that object into a client.DeleteAsync() call, as shown below.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.DeleteAsync(contactUri).Result;
return RedirectToAction("Index");
}
You can call your Web API from the client using jQuery ajax method. But since you are calling from a site other than where the Web API is deployed you will have to use JSONP, instead of JSON. Take a look at this QA to see how you use JSONP with Web API. Your models will be passed as JSON which you will have to render on the client side, instead of using Razor to render it on the server side. I would use something like Knockout to create a View Model on the client that will bind your model to the HTML elements on the client.

WebAPI service design

I'm pretty comfortable with how Asp.NET MVC controllers worked when designing services.
However the new WebAPI controllers. how am I supposed to design my services here?
Lets say we have 3 different ways to list e.g. Users.
Get 10 latest , Get all, Get inactive or whatever.
none of these might need parameters. so how would you solve this in WebAPI
IEnumerable<User> Get10Latest()
IEnumerable<User> GetAll()
IEnumerable<User> GetInactive()
That won't work since they have the same param signature.
So what is the correct way to design this here?
You can support multiple methods in one controller for a single HTTP method by using the action parameter.
E.g.
public class UsersController : ApiController
{
[ActionName("All")]
public HttpResponseMessage GetAll()
{
return new HttpResponseMessage();
}
[ActionName("MostIQ")]
public HttpResponseMessage GetMostIQ()
{
return new HttpResponseMessage();
}
[ActionName("TenLatest")]
public HttpResponseMessage GetTenLatest()
{
return new HttpResponseMessage();
}
}
Unfortunately, I have not found a way to get a single controller to handle both with and without the action at the same time.
e.g.
public class UsersController : ApiController
{
[ActionName("")] // Removing this attribute doesn't help
public HttpResponseMessage Get()
{
return new HttpResponseMessage();
}
[ActionName("All")]
public HttpResponseMessage GetAll()
{
return new HttpResponseMessage();
}
[ActionName("MostIQ")]
public HttpResponseMessage GetMostIQ()
{
return new HttpResponseMessage();
}
[ActionName("TenLatest")]
public HttpResponseMessage GetTenLatest()
{
return new HttpResponseMessage();
}
}
Being able to use a single controller for a collection resource and all of its subsets would be nice.
Someone will probably be along and wrap me on the knuckles for this, but you need to configure your routing to handle the Gets. This is how I got it working with the above operations:
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);
So now your requests are mapped to the correct controller -> action via the route template. Note that the new route needs to be registered first in WebApiConfig.cs. If you keep the old, default one.
EDIT
Having re-read the question I realize I wasn't quite answering the design question. I would think that one way to go about it, from a REST perspective, would be to use a separate resource to expose the proper collections (Get10Latest for example) since I assume that there is a business reason for exposing that exact subset of data through the service. In that case you'd expose that resource though a single Get in its own Controller (if that is the desired behaviour).
Well why not have urls like this:
GET /users
GET /users/latest
GET /users/inactive
Using routing you could route them to
public classs UserController : ApiController
{
public IEnumerable<User> Get(string mode)
{
// mode is in routing restricted to be either empty, latest, or inactive
}
}
Otherwise use multiple controllers. The use of action names in Web API is kind of a wrong way to about it.

Resources