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}"
Related
I'm new to the .NET framework and my company doesn't use Core yet, so I'm trying to figure out why my web application api is showing a 400. I had a normal web forms project and added a controller class named TagController.cs. My project is on port 44318 and I've tried accessing localhost/44318/api/tag with no luck. I also tried adding a controllers folder with api sub folder and the controller inside it, but to no avail. I've posted images of my project hierarchy and the errors themselves. I have a feeling that the project not having a global.asax could have something to do with it, but there is one in another project. Maybe TagController.cs is pointing to another port? Any help is greatly appreciated.
TagController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ClarityDBWebFormsRedis;
using StackExchange.Redis;
namespace ClarityDBWebFormsRedis
{
public class TagController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(string data) {
return "doge";
}
// POST api/<controller>
public void Post([FromBody] string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
You need a default (route) configuration in the project, so that it knows what it should do with the ApiControllers, or how the API can be called. This is defined for example in the Global.asax. You can simply put your class TagController into a folder called "Controllers".
The Global.asax looks then accordingly e.g. like this:
using System.Web.Http;
using System.Web.Routing;
(...)
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });
}
An ApiController for example looks like this:
public class PingController : ApiController
{
[HttpGet, AllowAnonymous]
public IHttpActionResult Get()
{
return Ok();
}
}
For normal pages it is enough to create an .Aspx page and then call it in the browser according to the created folder structure. If you use MVC, then this page is created in different files and folders in the project (Views/Home.cshtml, Models/HomeViewModel.cs and Controllers/HomeController.cs).
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. -.-
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.
I have found out how to version my WebAPI based on namespaces using this class.
I am using Swashbuckle to add Swagger doc to my API, using the Swashbuckle Nuget package.
If I keep everything intact, when I navigate to /swagger/, I get an empty page.
In my App_Start:
public class SwaggerConfig
{
public static void Register()
{
Bootstrapper.Init(GlobalConfiguration.Configuration);
SwaggerSpecConfig.Customize(c =>
{
c.IncludeXmlComments(GetXmlCommentsPath());
});
}
private static string GetXmlCommentsPath()
{
return string.Format(#"{0}\App_Data\XmlDocumentation.xml", AppDomain.CurrentDomain.BaseDirectory);
}
}
And my web API routes:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{namespace}/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
}
}
If I remove the {namespace} it works (the API commands are displayed), but I want to keep this namespace information in my route.
How do I customize Swagger/Swashbuckle to make this work?
From the Swashbuckle Github repo:
There is a flaw in the above implementation of "namespace routing" as it breaks the WebApi metadata layer - ApiExplorer and hence Swashbuckle.
A workaround, although doesn't directly solve your problem, is to use attribute versioning instead, which works fine with Swashbuckle:
I.e.:
[RoutePrefix("api/v1/Features")]
public class FeaturesV1Controller : ApiController
{
[Route("Products/{product}")]
public IList<Metadata.FeatureListItemModel> Get(long product){}
Please see the two Github issues below for more info.
https://github.com/domaindrivendev/Swashbuckle/issues/317
https://github.com/domaindrivendev/Swashbuckle/issues/303
I believe that with attribute routing, your controller must have a different name for each version. I.e. the class should be named FeaturesV1Controller and FeaturesV2Controller for v2, but for the routes you can still use /api/v1/Features and /api/v2/Features
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.