Consume ASP WebAPI from ActionResult in different controller - asp.net-mvc-3

What is the best way to consume WebAPI service from ActionResult in another controller?
Basicaly i have a controller that returns the data.
public class MyApiController : ApiController
{
public MyData Get()
{
//returns the data
}
}
and i want to consume this data from
public class MyConsumeController: Controller
{
public ActionResult MyConsumeAction()
{
var something = //Call Get inside MyApiController
}
}
Any ideas?
Note: I know how to consume it using HttpClient (different project) or jquery (from views).

You could use the client API - the HttpClient class. And here's another sample.

Related

Ajax not working after deploying to Azure

I am developing a web application using ASP.NET MVC Core. Everything works perfect on my local machine but whenever I deploy to Azure the Ajax calls always get a 404 Not Found.
Here's a snippet of one controller method:
[HttpGet]
public JsonResult GetPublicHolidays()
{
var events = adminService.GetPublicHolidays();
return new JsonResult(events);
}
And here's the Ajax call:
$.getJSON('#Url.Action("GetPublicHolidays","Admin")',
By default, the URL to an action in an ASP.NET Controller is not the name of the method. There's a lot going on by convention in ASP.NET.
As an example, this is a default ASP.NET Core API controller:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
As you can see in the comment, the route is <baseUrl>/api/values. This route is comprised of the base URL, the api prefix and the name of the controller. Because you add a HttpGetAttribute, ASP.NET knows that is the Get method.
So, considering this controller:
public class RandomController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<string>> WhateverWeirdMethodName()
{
return new string[] { "value1", "value2" };
}
}
The URL for the GET request would be <baseUrl>/api/random

Create ViewData outside Controler in MVC

I have a method inside a controller that creates ViewData. For example this one
private void CreateFKViewData(OSQDCOL osqdcol, OSADCOL osadcol, IList<OSADCOL> targetTBLcols)
{
...
ViewData[osadcol.ColName] = new SelectList(SelectListItems, "key", "value", SelectListItems.First().Key);
}
If that method is placed inside the controller everything works as expected. I want to move that method outside my controller and place it into a different class in my BLL layer. The problem is that ViewData is not accessible outside the scope of the controller.
Any ideas?
I'm assuming you're using ASP.NET MVC and C#. Not sure it's a great idea to spread around concerns that a controller would normally do into classes outside of the controller, but suffice to say that the reason for your issue is that ViewData is made available by the fact that your controller class inherits from Controller which in turn inherits from ControllerBase (which is where ViewData is provided). So let's say you wanted to call a method in another class from your controller, and you wanted that method to be able to manipulate ViewData. Consider the following:
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
var externalclass = new SomeRandomClass(this);
externalclass.DoStuff();
return View();
}
}
public class SomeRandomClass
{
ControllerBase _callingController = null;
public SomeRandomClass(ControllerBase callingController)
{
this._callingController = callingController;
}
public void DoStuff()
{
this._callingController.ViewData["hello"] = "world";
}
}

Attribute routing and inheritance

I am playing around with the idea of having a base controller that uses a generic repository to provide the basic CRUD methods for my API controllers so that I don't have to duplicate the same basic code in each new controller. But am running into problems with the routing attribute being recognized when it's in the base controller. To show exactly what the problem I'm having I've created a really simple WebAPI controller.
When I have a Get method in the main Controller and it inherits from the ApiController directly I don't have any problems and this works as expected.
[RoutePrefix("admin/test")]
public class TestController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
When I move the Get method into a base controller it is returning the contents of the 404 page.
[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{
}
public class TestBaseController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
Some more interesting notes:
I can access the action at GET /Test/1. So it is finding it based on the default route still.
When I try to access POST /admin/test, it returns the following JSON
{
"Message":"No HTTP resource was found that matches the request URI 'http://test.com/admin/test'.",
"MessageDetail":"No type was found that matches the controller named 'admin'."
}
Does anyone know of a way to get the routing to work with attributes from a base controller?
Attribute routes cannot be inherited. This was a deliberate design decision. We didn't feel right and didn't see valid scenarios where it would make sense to inherit them.
Could you give a more realistic scenario as to where you would want to use this?
[Update(3/24/2014)]
In the upcoming 5.2 release of MVC Web API, there is going to be an extensibility point called System.Web.Http.Routing.IDirectRouteProvider through which you can enable the inheritance scenario that you are looking for here. You could try this yourself using the latest night builds(documentation on how to use night builds is here)
[Update(7/31/2014)]
Example of how this can be done in Web API 2.2 release:
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
//---------
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
Using Web API 2.2, you can:
public class BaseController : ApiController
{
[Route("{id:int}")]
public string Get(int id)
{
return "Success:" + id;
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
as outlined here: http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22
Got it.
[Route("api/baseuploader/{action}")]
public abstract class BaseUploaderController : ApiController
{
[HttpGet]
public string UploadFile()
{
return "UploadFile";
}
}
[Route("api/values/{action}")]
public class ValuesController : BaseUploaderController
{
[HttpGet]
public string Get(int id)
{
return "value";
}
}
One caveat here is that the route action paramter must be the same as the action name. I could not find a way to get around that. (You cannot rename the route with a RouteAttribute)

Passing ViewModel to Web-Api action

Is it possible to pass an ViewModel object to WebApi controller action instead of separate params?
Instead of using:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(string p1, string p2)
{
// some logic
}
}
I would like to use:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(TestVM testVM)
{
// some logic
}
}
public class TestVM
{
public string P1 { get; set; }
public string P2 { get; set; }
}
This doesn't seem to work for me. When I call /api/contacts/?P1=aaa&P2=bbb the testVM object doesn't get populated (null).
Also, I would like the TestVM to have valdiation attribtues defined and use ModelState.IsValid in my API controller.
Unless told otherwise WebApi will deserialise complex models using the content/body of the request. To tell WebApi to use the Url to construct the model you need to specify the [FromUri] attribute:
public IEnumerable<Contact> GetContacts([FromUri]TestVM testVM)
{
// some logic
}
I know it's kind of late to post another answer but I thought it could be useful for anyone who uses .net core as a web API service
public IEnumerable<Contact> GetContacts([FromQuery]TestVM testVM)

MVC3 REST service - how do I access the request body content for a PUT or POST request?

I am creating an ASP.NET MVC3 restful web service to allow reports to be uploaded from a set of servers. When a new report is created, I want the client app to do a PUT to
http://MyApp/Servers/[ServerName]/Reports/[ReportTime]
passing the content of the report as XML in the body of the request.
My question is: how do I access the content of the report in my controller? I would imagine that it is available somewhere in the HttpContext.Request object but I am reluctant to access that from my controller as it is not possible(?) to unit test that. Is it possible to tweak the routing to allow the content to be passed as one or more parameters into the controller method? The outcome needs to be RESTful, i.e. it has to PUT or POST to a URL like the one above.
Currently my routing is:
routes.MapRoute(
"SaveReport",
"Servers/{serverName}/Reports/{reportTime",
new { controller = "Reports", action = "Put" },
new { httpMethod = new HttpMethodConstraint("PUT") });
Is there any way to modify this to pass content from the HTTP request body into the controller method?
The controller method is currently:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(string serverName, string reportTime)
{
// Code here to decode and save the report
}
}
The object I am trying to PUT to the URL is:
public class Report
{
public int SuccessCount { get; set; }
public int FailureOneCount { get; set; }
public int FailureTwoCount { get; set; }
// Other stuff
}
This question looks similar but doesn't have any answer.
Thanks in advance
Seems like you just need to use the standard ASP.NET MVC model binding capability with the slight wrinkle that you would doing an HTTP PUT instead of the more common HTTP POST. This article series has some good samples to see how model binding is used.
Your controller code would then look like:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(Report report, string serverName, string reportTime)
{
if (ModelState.IsValid)
{
// Do biz logic and return appropriate view
}
else
{
// Return invalid request handling "view"
}
}
}
EDIT: ====================>>>
Jon added this code to his comment as part of the fix so I added it to the answer for others:
Create a custom ModelBinder:
public class ReportModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var xs = new XmlSerializer(typeof(Report));
return (Report)xs.Deserialize(
controllerContext.HttpContext.Request.InputStream);
}
}
Modify the Global.asax.cs to register this model binder against the Report type:
ModelBinders.Binders[typeof(Report)] = new Models.ReportModelBinder();

Resources