Ive been struggling with this all day and i think ive reached the end of all the answers ive seen on this. Maybe im special, or maybe im doing something stupid.
I recently upgraded from V3 to V4 (i think). Im using the System.Web.OData namespace which should be fine.
Here is my WebAPIConfig
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using System.Web.OData.Routing;
using System.Web.OData.Routing.Conventions;
using GizmoAPI.Models;
namespace GizmoAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Counterparty>("Counterparties");
config.Select().Expand().Filter().OrderBy().MaxTop(null).Count();
config.EnableDependencyInjection();
config.MapHttpAttributeRoutes();
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
config.AddODataQueryFilter();
var cors = new System.Web.Http.Cors.EnableCorsAttribute("http://localhost:56248", "*", "*");
config.EnableCors( cors);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("text/html"));
config.EnableSystemDiagnosticsTracing();
}
}
}
and here is a basic controller:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.OData;
using System.Web.OData.Routing;
using GizmoAPI.Models;
namespace GizmoAPI.Controllers
{
public class CounterpartiesController : ODataController
{
private GizmoEntities db = new GizmoEntities();
// GET odata/Counterparties
[EnableQuery]
public IQueryable<Counterparty> GetCounterparties()
{
return db.Counterparties;
}
// GET odata/Counterparties(5)
[EnableQuery]
[ODataRoute("{key}")]
public SingleResult<Counterparty> GetCounterparty([FromODataUri] int key)
{
return SingleResult.Create(db.Counterparties.Where(counterparty => counterparty.CounterpartyID == key));
}
Then im trying to call the URL as http://localhost:60965/odata/Counterparties/101
or
http://localhost:60965/odata/101
or really anything at this point. I feel like there is some sort of configuration that im missing to activate it. I get the error "The path template '{key}' on the action 'GetCounterparty' in controller 'Counterparties' is not a valid OData path template. Resource not found for the segment '{key}'."
Did you try renaming your method to GetCounterParties([FromODataUri] int key)
and removing the OdataRoute Attribute?
This is not a 100% fix to my problem since i added odata as a tag.
Comparing the ODataController with the ApiController (WebAPI2.2), i couldnt find any reason not to switch to the latter.
So i changed my controller to inherit APIController, and switched all references of odata to the WebAPI version. In fact, the code above is almost identical with the exception of the aforementioned controller and the ODataRoute now becoming simply "Route". The code compiles completely fine and is far more manageable. I can even use the odata filters if the return type of my action is iqueryable.
Related
I have an ASP.NET MVC web application that I'm trying to add an API Controller to so that I can make front-end AJAX calls with jQuery.
I have seen several posts on stack overflow about how to add an API controller successfully to an MVC project and although I followed the instructions and have tried to avoid the common issues (I have added the required API dependencies and made sure that my API routes are being initialized before the MVC routes in my global.asax.cs)
My API Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace mywebsite.net.Controllers
{
public class WebAPIController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
My WebApi.config:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace mywebsite.net
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
My Global.asax.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Web.Http;
namespace mywebsite.net
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
When I try to send a GET request that I think should return information (in this case something like localhost:12345/api/WebAPI), nothing happens. The browser/http client just gets stuck loading.
Try putting a [HttpGet] above public IEnumerable<string> Get()
It turns out the API Controller was being served on a different port. -.-
When i try to invoke api controller the getting 404. But when i try to invoke Controller method its working fine. Please help and let me know if any information is required.
TPServicesAPIController.cs:-
using AgentVartualOffice.Models.LogIn;
using System.Web.Mvc;
using AgentVartualOffice.Models;
using Newtonsoft.Json;
using System.Text;
namespace AgentVartualOffice.Controllers.TPServices
{
public class TPServicesAPIController : ApiController
{
public string Myauth()
{
return "True";
}
}
}
Global.asax.cs:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Http;
namespace AgentVartualOffice
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalFilters.Filters.Add(new HandleErrorAttribute());
}
}
}
Invoke url:
http://localhost:61868/TPServicesAPI/Myauth
Chances are your are calling the wrong URL.
The default Web API convention-based routes are usually prefixed with api
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}", //<<--- default web API route template
defaults: new { id = RouteParameter.Optional }
);
}
}
and also do not use action names unless the route template has been changed.
routeTemplate: "api/{controller}/{action}/{id}"
So I suggest you check your API config and update the URL being called accordingly.
For example, by calling: api/TPServicesAPI/Myaut
To understand the code behind the AttributeRoutingConfig I'm trying to recreate it using RouteConfig
AttributeRoutingConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using AttributeRouting.Web.Mvc;
[assembly: WebActivator.PreApplicationStartMethod(typeof(SimplestAuth.AttributeRoutingConfig), "Start")]
namespace xyz
{
public static class AttributeRoutingConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapAttributeRoutes();
}
public static void Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
}
RoutingConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace xyz
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "login", id = UrlParameter.Optional }
);
}
}
}
I'm looking for information to understand the differences and similarities between the two techniques. Thank you.
They both do essentially the same thing:
They register your routes to the RouteTable.
The first code sample you have above utilizes AttributeRouting so it does an assembly scan.
The second code sample you have utilizes the RouteTable directly, so it is a much more manual looking process.
Hope that helps.
I'm playing with the "Microsoft.AspNet.WebApi.SelfHost" NuGet package in version "5.0.0-rc1" (tried also the current stable) but unfortunately I can't get my application setup to resolve the configured route to my implementation of the ApiController.
The application is quite simple and can be found nearly in all examples. It contains a .NET console application where in the main class the self host service lives.
using System.Web.Http.SelfHost;
using System.Web.Http;
using System;
namespace EP.Server
{
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:60064");
config.Routes.MapHttpRoute(
name: "Default Route",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Server ist running on "+config.BaseAddress + " hit ENTER to stop.");
Console.ReadLine();
server.CloseAsync().Wait();
}
}
}
}
and then there is a separate controller class which lives in the same project and inherits from the ApiController class.
using System.Web.Http;
namespace EP.Server
{
class UsersController : ApiController
{
[HttpGet]
public string Get()
{
return "Hello I'm a user...";
}
}
}
The error message sounds like this when I call the service from any browser.
<Error>
<Message>No HTTP resource was found that matches the request URI 'http://localhost:60064/api/Users'.</Message>
<MessageDetail>No type was found that matches the controller named 'Users'.</MessageDetail>
</Error>
Does somebody have an idea what's wrong there? Unfortunately I couldn't find any similar issues / questions on the web.
Thank you so far!
Web API controllers need to be public.
I am using the John Papa Single Page Application source code to create my own App and I am running into some problems when using the Breeze Web API. I have my own breeze controller and as soon as I add a second HttpGET method I get the error "Multiple actions were found that match the request".
It is Weird because in his code he adds multiple GETs and his code works but I think I am missing something.
Breeze Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Breeze.WebApi;
using AgencyUpdate.Models;
namespace AgencyUpdate.Controllers
{
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<AgencyDbContext> _ContextProvider =
new EFContextProvider<AgencyDbContext>();
public string MetaData()
{
return _ContextProvider.Metadata();
}
[HttpGet]
public IQueryable<api_Agency> GetAgency()
{
return _ContextProvider.Context.api_Agency;
}
[HttpGet]
public IQueryable<api_AgencyOffice> GetOffice()
{
return _ContextProvider.Context.api_AgencyOffice;
}
}
}
I use this URL to request data:
**http://localhost:13762/api/breeze/GetAgency**
Also I have found this .CS file for routing but I don't know if I have to make changes to it.
BreezeWebApiConfig
using System.Web.Http;
[assembly: WebActivator.PreApplicationStartMethod(
typeof(AgencyUpdate.App_Start.BreezeWebApiConfig), "RegisterBreezePreStart")]
namespace AgencyUpdate.App_Start {
///<summary>
/// Inserts the Breeze Web API controller route at the front of all Web API routes
///</summary>
///<remarks>
/// This class is discovered and run during startup; see
/// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
///</remarks>
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
}
}
Does anyone know what the problem is?
I feel a bit stupid the URL I need to use is breeze/breeze/MethodName.
John's code doesn't use breeze twice in the URL hence the confusion
Papa's course has the single-page-apps-jumpstart.zip file with project source code by chapters. The right version the BreezeWebApiConfig.cs content is as such:
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "api/{controller}/{action}"
);
}
}
}
Notice the string routeTemplate: "api/{controller}/{action}"