How would I redirect a from a RenderMvcController to UmbracoApiController - model-view-controller

A bit of sleep deprived experimentation here..
I've been playing around with Backbone.js recently and are wondering if there's a way to combine the client-side routing with the server-side routing of umbraco. They way I think about it is instead of the typical RenderMvcController ActionResult being returned for a template name in the route table, the controller would redirect to UmbracoApiController to return json - in effect handing off the task of routing to templates to the backbone client.
This is where I'm stuck, redirecting to the UmbracoApiController - hoping someone can see what I've missed.
Cheers!
Edit: Forgot the error message I'm getting.
No route in the route table matches the supplied values.
public class HomeController : Umbraco.Web.Mvc.RenderMvcController
{
[HttpGet]
public ActionResult Index()
{
return RedirectToAction("GetAllProducts", "ProductsApi");
}
}
public class ProductsApiController : Umbraco.Web.WebApi.UmbracoApiController
{
public IEnumerable<string> GetAllProducts()
{
return new[] { "Table", "Chair", "Desk", "Computer", "Beer fridge" };
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "Umbraco/Api/{controller}/{action}/",
defaults: new { id = RouteParameter.Optional }
);
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}

I got a similar thing working by extending PluginController rather than UmbracoApiController, following the approach here.

Related

Web API multiple get

I have couple of get methods in my web API class. The route config is as below;
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/test/{action}/{id}",
defaults: new { controller = "item", action="testBring", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The controller class is as below;
public class ItemController : ApiController
{
// GET: api/Item
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[ActionName("testBring")]
[HttpGet]
public string testBring()
{
return "value";
}
// GET: api/Item/5
public string Get(int id)
{
return "value";
}
// POST: api/Item
[HttpPost]
public void CreateItem([FromBody]string value)
{
}
// PUT: api/Item/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Item/5
public void Delete(int id)
{
}
}
When I try to call /api/test, testBring method gets called. But when I call /api/Item I get an error that multiple actions were found that match the request ... Why is it so?
/api/test maps fine to the first route template api/test/{action}/{id} because of the default parameters included, which allow it to call a specific action.
However, /api/Item maps to the second route template api/{controller}/{id} and the route table is unable to determine which action you wanted since there are multiple HttpGet actions (ie Get() and testBring() ) that match the requested path.

How do I route to a method in ASP.NET Web API?

According to the documentation, if I have this in my WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I should be able to route to a method in my API controller using a URL like this:
http://localhost:55601/api/Customers/Search
Here is my method:
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult Search([FromBody]CustomerDTO SearchTerm)
{
string Name = SearchTerm.Name;
string Email = SearchTerm.Email;
string PhoneNumber = SearchTerm.Phone;
var customer = db.Customers.Single(c => c.Name == Name && c.EmailAddress == Email && c.PhoneNumber == PhoneNumber);
return Ok(customer.id);
}
I'm sending the search data as a JSON object (using HTTP POST method) in the request body.
However, I get an error saying:
Multiple actions were found that match the request
I only have one method in this controller called Search.
I would have thought this should be pretty straightforward, and work the same way it does with MVC controllers. But I think I'm missing something obvious. Can anyone tell me what it is?
EDIT: As per #KevinLaw's request, adding code for controller showing upblic methods. Also, for further information the following request (HTTP GET) works as expected:
http://localhost:55601/api/Customers?email=[recipient#domain]
public class CustomersController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: api/Customers
public IQueryable<Customer> GetCustomers()
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(int id)
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomerByEmail(string email)
{
//...
}
// PUT: api/Customers/5
[ResponseType(typeof(void))]
public IHttpActionResult PutCustomer(int id, Customer customer)
{
//...
}
// POST: api/Customers
[ResponseType(typeof(Customer))]
public IHttpActionResult PostCustomer(Customer customer)
{
//...
}
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult SearchCustomer([FromBody]CustomerDTO SearchTerm)
{
//...
}
// DELETE: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult DeleteCustomer(int id)
{
//...
}
}
The problem here is that the WebApiController uses the REST API specs.
Which state that in a Web Api Controller there can be Zero - One Http Verb.
What i mean by that is that you can have one GET,PUT,POST,DELETE,PATCH
The reason you don't have any problem with the GET is because you have them correctly overloaded. () (int) (string).
But your Posts is (Customer) (CustomerDTO) They are both complex objects and the Binder cannot identify which is which when binding to the complex object.
For this to work you need to use Route Attributes or explicit route.
Attribute Routing
Explicit Routing pt1
Explicit Routing pt2
I think the links are enough to get you started.
If you still want to see some code on your specific case leave a comment below and i will give you some examples.
Thanks
EDIT: Added Examples
Attribute Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
}
On Controller
[RoutePrefix("api/test")]
public class TestingController : ApiController
{
[HttpGet]
[Route("")]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
[Route("search")]
public IHttpActionResult Post([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}
Explicit Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes config.Routes.MapHttpRoute(
name: "SearchTest",
routeTemplate: "api/test/search",
defaults: new { controller = "Testing", action = "Search" }
);
config.Routes.MapHttpRoute(
name: "TestingController",
routeTemplate: "api/test/{id}",
defaults: new { controller = "Testing", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
On Controller
public class TestingController : ApiController
{
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
public IHttpActionResult Search([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}

Web api 2 attribute routing not working even with it configured in WebApiConfig

I don't understand why my attribute routing isn't working. If I go to 'http://localhost:56125/api/Contact/1682' then I get the test controller with "value1" and "value2" shown, but if I go to http://localhost:56125/api/contacts/1682 then I get No HTTP resource was found that matches the request URI 'http://localhost:56125/api/contacts/1682' and I don't understand why?
In ContactController.cs:
public class ContactController : ApiController
{
private readonly NGSystemRepository _repo = new NGSystemRepository();
// GET: api/Contact
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactNumber)
{
return _repo.GetContact(contactNumber);
}
}
In WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Argh! Stupid mistake. I didn't match the attribute property name (contactId) with the method property name (contactNumber).
My controller action should simply be:
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactId)
{
return _repo.GetContact(contactNumber);
}

Post data through Url to webapi

Hej,
I am unable to post data to the action method through querystring to the action method which is located in the controller class below is my code.
I type a url "http://localhost:53459/api/esb/post/test" to post value and nothing happens
Any help would be appreciated.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}
[Route("put")]
[HttpPut]
public string Put([FromUri] string value)
{
return string.Format("{0} is updated successfully ", value);
}
[Route("delete")]
[HttpDelete]
public string Delete(string value)
{
return string.Format("{0} is deleted successfully ", value);
}
}
If you are typing the url into a browser you are constructing a GET request so it will never reach your Post action. You can confirm this by adding "GET" to the allowed verbs on the action (note: remove the [HttpPost] attribute).
[Route("post")]
[AcceptVerbs("POST", "GET")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}
remove parameter binding [FromUri] and update the Route like
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
Controller
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post/{value}")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post(string value)
{
return string.Format("{0} is posted successfully ", value);
}
}
This is working for me, try to use Postman in chrome or fiddler.
POST http://localhost:XXXXX/api/esb/post/test HTTP/1.1
For dot.Net Core (I'm using v2.0), use FromRoute
[AllowAnonymous]
[HttpPost]
[Route("validateEmail/{validationCode}")]
public async Task<IActionResult> ValidateEmail(
[FromRoute] Guid validationCode)
{
await authService.ValidateEmailAsync(validationCode);
return Ok();
}
Then post like this:

Two GET in MVC C#

public class EmployeesController : ApiController
{
public IEnumerable<Employee> GetAllEmployees()
{
return (new EmployeeData()).GetEmployeeRecords();
}
public Employee GetSingleEmployee(int id)
{
return (new EmployeeData()).GetSingleEmployeeRecord(id);
}
public void Delete(int id)
{
(new EmployeeData()).DeleteEmployeeRecord(id);
}
}
This is what I have now. I'm trying to make the app call the first function with a get call api/employee and the second one with a get call api/employee/(an ID number) eg api/employee/75. The get call always goes to the first one. How do I solve this?
This is my routing:
namespace EmployeeApp
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id=UrlParameter.Optional }
);
}
}
}
After a brief discussion in chat, we discovered that the issue here is that you have two different route configuration files, one for MVC and one for WebAPI. While you could change the name of your actions to match the convention of the WebAPI route configuration (i.e. change GetSingleEmployee to GetEmployeeById), there is another way which would allow you to keep the actions named the same, and have more control over your desired routes. My recommendation is to use the newer Attribute Routing syntax.
First, change your configuration class like so:
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
next, change your class signature to define the routes through attributes:
[RoutePrefix("api/employee")]
public class EmployeesController : ApiController
{
// http://example.com/api/employee/
[Route("")]
public IEnumerable<Employee> GetAllEmployees()
{
return (new EmployeeData()).GetEmployeeRecords();
}
// http://example.com/api/employee/75
[Route("{id:int}"]
public Employee GetSingleEmployee(int id)
{
return (new EmployeeData()).GetSingleEmployeeRecord(id);
}
// http://example.com/api/employee/Delete/75
[Route("Delete/{id:int}")]
public void Delete(int id)
{
(new EmployeeData()).DeleteEmployeeRecord(id);
}
}
This controller now clearly shows it's routes, rather than the routes being defined somewhere else, or being defined by an obscure name matching convention.

Resources