ASP.NET MVC 4 Application Calling Remote WebAPI - asp.net-mvc

I've created a couple ASP.NET MVC applications in the past, but I've never used WebAPIs before. I'm wondering how I could create a simple MVC 4 app that does simple CRUD stuff via WebAPI instead of through a normal MVC controller. The trick is that the WebAPI should be a separate solution (and, in fact, could very well be on a different server/domain).
How do I do that? What am I missing? Is it just a matter of setting up routes to point to the WebAPI's server? All the examples I've found showing how to consume WebAPIs using an MVC application seem to assume the WebAPI is "baked in" to the MVC application, or at least is on the same server.
Oh, and to clarify, I'm not talking about Ajax calls using jQuery... I mean that the MVC application's controller should use the WebAPI to get/put data.

You should use new HttpClient to consume your HTTP APIs. What I can additionally advise you to make your calls fully asynchronous. As ASP.NET MVC controller actions support Task-based Asynchronous Programming model, it is pretty powerful and easy.
Here is an overly simplified example. The following code is the helper class for a sample request:
public class CarRESTService {
readonly string uri = "http://localhost:2236/api/cars";
public async Task<List<Car>> GetCarsAsync() {
using (HttpClient httpClient = new HttpClient()) {
return JsonConvert.DeserializeObject<List<Car>>(
await httpClient.GetStringAsync(uri)
);
}
}
}
Then, I can consume that through my MVC controller asynchronously as below:
public class HomeController : Controller {
private CarRESTService service = new CarRESTService();
public async Task<ActionResult> Index() {
return View("index",
await service.GetCarsAsync()
);
}
}
You can have a look at the below post to see the effects of asynchronous I/O operations with ASP.NET MVC:
My Take on Task-based Asynchronous Programming in C# 5.0 and ASP.NET MVC Web Applications

Thanks everyone for the responses. #tugberk led me down the right path, I think. This worked for me...
For my CarsRESTService helper:
public class CarsRESTService
{
readonly string baseUri = "http://localhost:9661/api/cars/";
public List<Car> GetCars()
{
string uri = baseUri;
using (HttpClient httpClient = new HttpClient())
{
Task<String> response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<List<Car>>(response.Result).Result;
}
}
public Car GetCarById(int id)
{
string uri = baseUri + id;
using (HttpClient httpClient = new HttpClient())
{
Task<String> response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<Car>(response.Result).Result;
}
}
}
And then for CarsController.cs:
public class CarsController : Controller
{
private CarsRESTService carsService = new CarsRESTService();
//
// GET: /Cars/
public ActionResult Index()
{
return View(carsService.GetCars());
}
//
// GET: /Cars/Details/5
public ActionResult Details(int id = 0)
{
Car car = carsService.GetCarById(id);
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
}

You can use WCF to consume the service. Like so:
[ServiceContract]
public interface IDogService
{
[OperationContract]
[WebGet(UriTemplate = "/api/dog")]
IEnumerable<Dog> List();
}
public class DogServiceClient : ClientBase<IDogService>, IDogService
{
public DogServiceClient(string endpointConfigurationName) : base(endpointConfigurationName)
{
}
public IEnumerable<Dog> List()
{
return Channel.List();
}
}
And then you can consume it in your controller:
public class HomeController : Controller
{
public HomeController()
{
}
public ActionResult List()
{
var service = new DogServiceClient("YourEndpoint");
var dogs = service.List();
return View(dogs);
}
}
And in your web.config you place the configuration for your endpoint:
<system.serviceModel>
<client>
<endpoint address="http://localhost/DogService" binding="webHttpBinding"
bindingConfiguration="" behaviorConfiguration="DogServiceConfig"
contract="IDogService" name="YourEndpoint" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="DogServiceConfig">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>

http://restsharp.org/ is the answer to your questions. I am currently using it in an application which has similar structure.
But more generally using WebAPI is just posting and requesting data how to process is up to you. You can even use standard WebRequest and JavascriptSerializer.
Cheers.

In this case, you can use HttpClient to consume Web API from your controller.

Related

What is the correct way of consuming a web api if it is separated

//controller
[HttpPost]
public ActionResult Create(List<Music> musicList)
{
var x = Post.Add(musicList); <--ivegoterrorhere
return View();
}
//webAPI
public class MusicController : ApiController
{
// POST: api/Music
public static string Post(List<Music> musicList)
{
MusicBusiness.addMusic(musicList);
return "successful";
}
}
What is the correct way of consuming a web api if it is separated from the main web application?
My solution has two projects, webAPI and
The web Application project is going to call the webAPI project.

How to pass data from custom authorize attribute to web api action method?

I am creating a MVC Web API application with forms authentication for the Web API Controllers as well as the regular MVC controllers. In the form authentication cookie I am storing user information which I need to read and pass it to the Web API action methods.
I am trying to do this by creating a custom Authorization attribute and adding this value to ActionArguments. However I don't know how to access this data in the Web API action. Below is the code block
public class MyAuthorization : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var isAuthorised = base.IsAuthorized(actionContext);
if (isAuthorised)
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var identity = new GenericIdentity(ticket.Name);
actionContext.ActionArguments["UserInfo"] = ticket.UserData;
}
}
}
And here is the controller code
[RoutePrefix("api/test")]
[MyAuthorization]
public class TestWebAPIController : ApiController
{
[HttpGet]
[Route("")]
public IHttpActionResult Get() {
//How to get it here
return Ok();
}
}
It was really stupid of me. As the name suggests it is the ActionArgument (actionContext.ActionArguments). So this can be easily accessed in the Web API control by means of an ActionContext.ActionArguments or directly by passing an action argument.
Here is code sample by ActionContext.ActionArguments
public IHttpActionResult Post() {
var userInfo = ActionContext.ActionArguments["UserInfo"];
return Ok();
}
And here is by means of action argument
public IHttpActionResult Post(UserInfo userinfo) {
//Code here
return Ok();
}

OData V3 action in ASP.NET web api doesn't get trigger

I am using OData V3 endpoints using asp.net with webapi 2.2. I have successfully implemented CRUD operation with it. Now, I would like to add some custom actions along with CRUD operations. I have followed the article ( http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/odata-actions ) to create the action with OData V3 with web api.
When I type
URI:
http://localhost:55351/odata/Courses(1101)/AlterCredits
it throws following error:
<m:error><m:code/><m:message xml:lang="en-US">No HTTP resource was found that matches the request URI 'http://localhost:55351/odata/Courses(1101)/AlterCredits'.</m:message><m:innererror><m:message>No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.</m:message><m:type/><m:stacktrace/></m:innererror></m:error>
I have also tried adding a custom route convetion for non-bindable actions. (https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataActionsSample/ODataActionsSample/App_Start/WebApiConfig.cs ) Not sure if I have to use this.
Here is my code:
WebApiConfig.cs :---
namespace ODataV3Service
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
IList<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault(); //Do I need this?
//conventions.Insert(0, new NonBindableActionRoutingConvention("NonBindableActions"));
// Web API routes
config.Routes.MapODataRoute("ODataRoute","odata", GetModel(), new DefaultODataPathHandler(), conventions);
}
private static IEdmModel GetModel()
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.ContainerName = "CollegeContainer";
modelBuilder.EntitySet<Course>("Courses");
modelBuilder.EntitySet<Department>("Departments");
//URI: ~/odata/Course/AlterCredits
ActionConfiguration atlerCredits = modelBuilder.Entity<Course>().Collection.Action("AlterCredits");
atlerCredits.Parameter<int>("Credit");
atlerCredits.Returns<int>();
return modelBuilder.GetEdmModel();
}
}
}
CoursesController.cs:----
[HttpPost]
//[ODataRoute("AlterCredits(key={key},credit={credit})")]
public async Task<IHttpActionResult> AlterCredits([FromODataUri] int key, ODataActionParameters parameters)
{
if (!ModelState.IsValid)
return BadRequest();
Course course = await db.Courses.FindAsync(key);
if (course == null)
{
return NotFound();
}
int credits = course.Credits + 3;
return Ok(credits);
}
Global.asax:----
namespace ODataV3Service
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
I have done research online and found this link. Web API and OData- Pass Multiple Parameters But this one is for OData V4. I am using OData V3 and Action.
Thanks,
First, your action AlterCredits is defined as:
ActionConfiguration atlerCredits = modelBuilder.Entity<Course>().Collection.Action("AlterCredits");
It means AlterCredits bind to the collection of Course.
Second, your method AlterCredits in your controller is defined as:
public async Task<IHttpActionResult> AlterCredits([FromODataUri] int key, ODataActionParameters parameters)
{
...
}
It means AlterCredits listen to the call on the entity of Course.
Therefore, you got the No HTTP resource was found error message.
Based on your sample code, I create a sample method for your reference:
[HttpPost]
public async Task<IHttpActionResult> AlterCredits(ODataActionParameters parameters)
{
if (!ModelState.IsValid)
return BadRequest();
object value;
if (parameters.TryGetValue("Credit", out value))
{
int credits = (int)value;
credits = credits + 3;
return Ok(credits);
}
return NotFound();
}
Then, if you send a request:
POST ~/odata/Courses/AlterCredits
Content-Type: application/json;odata=verbose
Content: {"Credit":9}
You can get a response like this:
{
"d":{
"AlterCredits":12
}
}
For your questions:
IList conventions = ODataRoutingConventions.CreateDefault(); //Do I need this?
Answer: No, you needn't. Just using the default as:
config.Routes.MapODataServiceRoute("ODataRoute", "odata", GetModel());
//[ODataRoute("AlterCredits(key={key},credit={credit})")]
Answer: No, you needn't the ODataRouteAttribute for bind action.
Thanks.

Injecting a value from MVC controllers

Our repositories and services are currently being injected into our controllers via a Unity container (using the Web API MVC bootstrapper).
public class AnnouncementsController : BaseApiController
{
protected IAnnouncementRepository AnnouncementRepository{ get; set; }
public AnnouncementsController (IAnnouncementRepository announcementRepository)
: base()
{
this.AnnouncementRepository = announcementRepository;
}
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AnnouncementType);
// ...
}
}
A new requirement has arisen: All input models (e.g. GetAnnouncementsModel) now need to have an AccessToken.
Why? So that results from repositories are filtered according to data rights. Clients are restriction on what data they can consume.
Bad Solution - Pass in token as a method parameter
One solution is to include an AccessToken parameter to every repository or service call. This is not a good solution. We have to implement this in hundreds of methods. An example of this parameter:
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AccessToken, model.AnnouncementType);
// ...
}
Better Solution - Inject token during resolution
A better solution would be to provide the AccessToken in the repository constructors and have some base implementation that does the filtering logic implicitly.
But how could I do this with dependency injection? The constructor is resolved and called by the Unity container. How could I inject the property value of an input model into this process?
container.RegisterType<IAnnouncementRepository, AnnouncementRepository>(
new InjectionConstructor(
new InjectionParameter<Guid>(AccessToken)
)
);
You can define a custom interface, call it for example IAccessTokenProvider:
interface IAccessTokenProvider
{
Guid Token { get; }
}
Now you can make an implementation like this:
public class HttpContextAccessTokenProvider
{
public Guid Token
{
get { return (Guid)HttpContext.Current.Items["AccessToken"]; }
}
public static void SetToken(Guid token)
{
HttpContext.Current.Items["AccessToken"] = token;
}
}
Now you should be able to implement a filter to read the token from the request:
public class TokenFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string tokenString = filterContext.HttpContext.Request.QueryString["token"];
ActionExecutingContext.SetToken(Guid.Parse(tokenString));
}
}
You can also read the token from other sources or store it in other containers (sessions, cookies, whatever). You can also directly access it in your controller or repositories.
You have 2 options to use the token in your repository:
Inject IAccessTokenProvider to your repository and get the token directly from the provider.
Inject IAccessTokenProvider to your controller and pass the token

I want to use session in the asp.net mvc controller constructor

I'm new to Mvc.
Sorry to my english. ^^
I have some question about asp.net MVC session in the controller.
The Scenario things that I want to do is like follows..
First of all, My development circumstance is entityframework and mvc3.
When Someone logged in each one has different database. So, Each has connect different database.
So, Each person has his own session value which is database connection string. So far so good.
I have simple database Repository and at the each repository's constructor can change database connection.
At controller which calls Repository class, I need session value. But As I know Controller's construction can't keep session value. right?
I want your good advice. Thanks in advance.
Code samples are below:
public class MasterRepository
{
DBEntities _db;
public MasterRepository(string con)
{
_db = new DBEntities(con);
}
}
public class TestController : Controller
{
private string con;
MasterRepository _db;
public TestController()
{
_db = new MasterRepository(Session["conn"].ToString()); // Session is null I want to solve this Part...
}
public ActionResult Index()
{
string con = Session["conn"].ToString(); // Session is assigned.
return View();
}
}
These should explain what's happening to cause Session to be null, and give you a few possible solution options:
Is ASP.NET MVC Session available at any point durign controller construction
Why my session variables are not available at construction of a Controller?
Session null in ASP.Net MVC Controller Constructors
I think you have missed out the "service" part of the controller - service - repository pattern:
http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx
But when you go down this path you will probably also need to learn IoC as well.
Then your code would look more like:
public class MasterRepository
{
public Foo GetAllFoo()
{
return ObjectContextManager.GetObjectContext().AsQueryable().ToList();
}
}
public class MasterService
{
MasterRepository _repository;
public MasterService(MasterRepository repository) // use IoC
{
_repository = repository;
}
public Foo GetAllFoo()
{
return _repository.GetAllFoo();
}
}
public class TestController : Controller
{
MasterService _service;
public TestController(MasterService service) // use IoC
{
_service = service;
}
public ActionResult Index()
{
var model _service.GetAllFoo();
return View(model);
}
}

Resources