I have a webforms project that supports MVC. I have created a WEB API 2 controller that used a model class which was generated using EF database first to convert SQL table into entity model. when I launch my application, the controller doesn't work as expected. I get the following when try to access the api:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
My Controller class is as following:
namespace YuClone.Controllers {
public class VideosController : ApiController
{
private YuCloneContext db = new YuCloneContext();
// GET: api/Videos
public IQueryable<video> Getvideos()
{
return db.videos;
}
// GET: api/Videos/5
[ResponseType(typeof(video))]
public IHttpActionResult Getvideo(long id)
{
video video = db.videos.Find(id);
if (video == null)
{
return NotFound();
}
return Ok(video);
}
// PUT: api/Videos/5
[ResponseType(typeof(void))]
public IHttpActionResult Putvideo(long id, video video)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != video.videoid)
{
return BadRequest();
}
db.Entry(video).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!videoExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Videos
[ResponseType(typeof(video))]
public IHttpActionResult Postvideo(video video)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.videos.Add(video);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = video.videoid }, video);
}
// DELETE: api/Videos/5
[ResponseType(typeof(video))]
public IHttpActionResult Deletevideo(long id)
{
video video = db.videos.Find(id);
if (video == null)
{
return NotFound();
}
db.videos.Remove(video);
db.SaveChanges();
return Ok(video);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool videoExists(long id)
{
return db.videos.Count(e => e.videoid == id) > 0;
}
} }
What can be done to fix this?
Edit:
This is how the route is configured in WebApiConfig :
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 }
);
}
}
which is registered in application_start method in Global.aspx as :
RouteConfig.RegisterRoutes(RouteTable.Routes);
The URL I'm using to access the resource is :
http://localhost:5958/api/videos
You need to register your WebAPI routes in Global.asax.cs:
GlobalConfiguration.Configure(WebApiConfig.Register);
You are not registering the WebAPI routes.
RouteConfig.RegisterRoutes(RouteTable.Routes);
This code only registers MVC controller routes. For Web API, you need to register web api routes by calling the class you mentioned -
var config = GlobalConfiguration.Configuration;
WebApiConfig.Register(config); //register api routes
RouteConfig.RegisterRoutes(RouteTable.Routes);
This should work for you.
Make sure you are calling the RouteConfig.RegisterRoutes after the WebApiConfig.Register. Like this:
System.Web.Http.GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
Related
I'm making a very simple Web API using MVC.NET to retrieve values from the following database:
CREATE TABLE [dbo].[Rates] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Code] VARCHAR (3) NOT NULL,
[Name] VARCHAR (50) NOT NULL,
[Rate] DECIMAL (5, 2) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
For whatever reason that I do not understand, whenever I compile my solution and navigate to localhost:xxxxx/api or api/Rates (my controller), I get the following error:
Server Error in '/' Application
The Resource cannot be found. (A Http 404 error)
I do not understand why this is happening, as it's a freshly built api application, using Entity Framework.
Below are my controllers and WebApiConfig classes. Perhaps something in one of those is amiss?
WebApiConfig:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
using System.Net.Http.Headers;
namespace ExchangeService
{
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: "localhost:63484/api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}
}
}
ValuesController (Left as default)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ExchangeService.Controllers
{
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
And finally, my Rates 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.Description;
using ExchangeService.Models;
namespace ExchangeService.Controllers
{
public class RatesController : ApiController
{
private ExRatesDBEntities db = new ExRatesDBEntities();
// GET: api/Rates
public IQueryable<Rate> GetRates()
{
return db.Rates;
}
// GET: api/Rates/5
[ResponseType(typeof(Rate))]
public IHttpActionResult GetRate(int id)
{
Rate rate = db.Rates.Find(id);
if (rate == null)
{
return NotFound();
}
return Ok(rate);
}
// PUT: api/Rates/5
[ResponseType(typeof(void))]
public IHttpActionResult PutRate(int id, Rate rate)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != rate.Id)
{
return BadRequest();
}
db.Entry(rate).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!RateExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Rates
[ResponseType(typeof(Rate))]
public IHttpActionResult PostRate(Rate rate)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Rates.Add(rate);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = rate.Id }, rate);
}
// DELETE: api/Rates/5
[ResponseType(typeof(Rate))]
public IHttpActionResult DeleteRate(int id)
{
Rate rate = db.Rates.Find(id);
if (rate == null)
{
return NotFound();
}
db.Rates.Remove(rate);
db.SaveChanges();
return Ok(rate);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool RateExists(int id)
{
return db.Rates.Count(e => e.Id == id) > 0;
}
}
}
The only other point of interest I can think of, is that this application is running from an external hard drive. I can't think of any reason why this should be an issue, but thought it would be worth noting. Thanks.
For whatever reason that I do not understand, whenever I compile my solution and navigate to localhost:xxxxx/api or api/Rates (my controller), I get the following error:
Server Error in '/' Application
The Resource cannot be found. (A Http 404 error)
In the first case it cause, because you didn't specify API controller, in the second case, because you didn't specify method of API controller.
Try to call it as http://localhost:63484/api/Rates/GetRates
UPDATE:
Looks like you haven't registered your routes correctly, since you are using both MVC and Web API, so try these configurations:
WebApiConfig class:
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 }
);
}
}
RouteConfig class:
public static 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 = "Index", id = UrlParameter.Optional }
);
}
}
And then call them in your Global.asax class:
protected void Application_Start()
{
...
//next line registers web api routes
GlobalConfiguration.Configure(WebApiConfig.Register);
...
//next line registers mvc routes
RouteConfig.RegisterRoutes(RouteTable.Routes);
...
}
I don't believe you need to list the port.
Change the following in your WebApiConfig:
routeTemplate: "localhost:63484/api/{controller}/{id}"
to
routeTemplate: "api/{controller}/{id}"
Try also renaming:
GetRates() to Get()
and calling:
http://localhost:63484/api/Rates
For a rate with an Id you will need to make the following changes:
// GET: api/Rates/5
[ResponseType(typeof(Rate))]
public IHttpActionResult GetRate(int id)
{
Rate rate = db.Rates.Find(id);
if (rate == null)
{
return NotFound();
}
return Ok(rate);
}
to
// GET: api/Rates/5
[ResponseType(typeof(Rate))]
public IHttpActionResult Get(int id)
{
Rate rate = db.Rates.Find(id);
if (rate == null)
{
return NotFound();
}
return Ok(rate);
}
Actually all your Actions in your RateController need to be renamed. Use the same naming convention as you did in your ValuesController. WepAPI is meant to operate off of the named Actions Get(), Put(), Post() etc.
I'm currently trying to use Swashbuckle 5.4.0 inside my ASP.NET MVC Web API application. Whenever I go to the swagger generated page (via rooturl/swagger) the page is correctly loaded, but no methods are shown. Comparing my project with another one which correctly works, I can find any difference in the code. What am I missing?
Here you are the .cs files related to the problem:
SwaggerConfig.cs:
namespace WebApiExperiment
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "Swagger Demo Api");
c.IncludeXmlComments(string.Format(#"{0}\bin\WebApiExperiment.XML",
System.AppDomain.CurrentDomain.BaseDirectory));
})
.EnableSwaggerUi();
}
}
}
Startup.cs
[assembly: OwinStartupAttribute(typeof(WebApiExperiment.Startup))]
namespace WebApiExperiment
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
Global.asax
namespace WebApiExperiment
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
HomeController.cs (the easiest and dumbest controller inside my project)
namespace WebApiExperiment.Controllers
{
public class HomeController : ApiController
{
public IHttpActionResult Index(SendCodeViewModel sendView)
{
return Ok();
}
public IHttpActionResult About(SendCodeViewModel sendView)
{
return Ok("Your application description page.");
}
public IHttpActionResult Contact(SendCodeViewModel sendView)
{
return Ok("Your contact page.");
}
}
}
For any file, just let me know and I'll provide you.
Short answere: Routing matters. How swagger can generate documentation without knowledge of routing in your API?
Try this:
namespace WebApiExperiment.Controllers
{
[RoutePrefix("api/routingMatters")]
public class HomeController : ApiController
{
[Route("magic")]
public IHttpActionResult Index(SendCodeViewModel sendView)
{
return Ok();
}
[Route("magic2")]
public IHttpActionResult About(SendCodeViewModel sendView)
{
return Ok("Your application description page.");
}
[Route("magic3")]
public IHttpActionResult Contact(SendCodeViewModel sendView)
{
return Ok("Your contact page.");
}
}
}
I have the following (standard) 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 }
);
}
and the following api controller:
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/Books
[Route("")]
public IQueryable<string> GetBooks()
{
return null;
}
// GET api/Books/5
[Route("{id:int}")]
public async Task<IHttpActionResult> GetBook(int id)
{
return Ok();
}
[Route("{id:int}/details")]
public async Task<IHttpActionResult> GetBookDetail(int id)
{
return Ok();
}
[Route("abc")]
public IQueryable<string> GetBooksByGenre(string genre)
{
return null;
}
[Route("~api/authors/{authorId}/books")]
public IQueryable<string> GetBooksByAuthor(int authorId)
{
return null;
}
}
It found appropriate method when I call
api/books
api/books/1
api/books/1/details
but it can't find api/books/abc.
If I change [Route("abc")] to [Route("{genre}")] it works (pass abc as genre parameter).
But I need to have many GET methods with different names.
What did I do wrong?
Try
// GET api/Books/genres/horror
[Route("genres/{genre}")]
public IQueryable<string> GetBooksByGenre(string genre)
{
return null;
}
Or even
// GET api/genres/horror/books
[Route("~api/genres/{genre}/books")]
public IQueryable<string> GetBooksByGenre(string genre)
{
return null;
}
I've been banging my head on trying to get ViewModels to validate with webapi 2.2
From the docs ..it should work:
http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api
namespace WebApplication3.Controllers
{
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
public class TestViewModel
{
[Required]
[EmailAddress]
[MinLength(3)]
[MaxLength(255)]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
public class ValuesController : ApiController
{
[ValidateModel]
[HttpGet]
public string Test(TestViewModel email)
{
if (ModelState.IsValid)
{
return "ok";
}
return "not ok";
}
}
}
With or without the ValidateModelAttribute it just returns "ok" all the time...
The ValidateModelAttribute is registered in WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Filters.Add(new ValidateModelAttribute());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Anyone have an idea whats going on here ? It's so much simpler to use DataAnnotations to prevalidate data.
Sample request:
http://localhost:55788/api/values/Test?email=ss
Returns: ok
Neither GET/POST changes anything
Where simple MVC controllers seem to have no problem, in this web api example we apparently have to specify [FromUri]
This works just fine
[HttpGet]
public string Test([FromUri]TestViewModel email)
{
if (ModelState.IsValid)
{
return "ok";
}
return "not ok";
}
With this code i can now also implement jsonP behavior
Also the custom ValidateModelAttribute becomes obsolete, though it can still be useful if you want to throw an exception systematically when a ViewModel is invalid. I rather just handle it in code to be able to return custom error objects.
I'm trying to build a small application, a api to get data with entity framework and pas out to json with web api but get the error: {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:61267/api/GetCarousel'.","MessageDetail":"No type was found that matches the controller named 'GetCarousel'."}
Call link: http://localhost:61267/api/GetCarousel
I have learn when goggled that it seem to be many solutions to this but non seems to fit mine.
WebApiConfig file
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
}
GetCarousel.cs Controller file
This is generated from database with help of Entity Framework
namespace MustWebAPI.Controller
{
public class GetCarousel : ApiController
{
private MustTestEntitie db = new MustTestEntitie();
// GET: api/GetCarousel
public IQueryable<GetCarousel_Result> GetGetCarousel_Result()
{
return db.GetCarousel_Result;
}
// GET: api/GetCarousel/5
[ResponseType(typeof(GetCarousel_Result))]
public IHttpActionResult GetGetCarousel_Result(int id)
{
GetCarousel_Result getCarousel_Result = db.GetCarousel_Result.Find(id);
if (getCarousel_Result == null)
{
return NotFound();
}
return Ok(getCarousel_Result);
}
// PUT: api/GetCarousel/5
[ResponseType(typeof(void))]
public IHttpActionResult PutGetCarousel_Result(int id, GetCarousel_Result getCarousel_Result)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != getCarousel_Result.Id)
{
return BadRequest();
}
db.Entry(getCarousel_Result).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!GetCarousel_ResultExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/GetCarousel
[ResponseType(typeof(GetCarousel_Result))]
public IHttpActionResult PostGetCarousel_Result(GetCarousel_Result getCarousel_Result)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.GetCarousel_Result.Add(getCarousel_Result);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = getCarousel_Result.Id }, getCarousel_Result);
}
// DELETE: api/GetCarousel/5
[ResponseType(typeof(GetCarousel_Result))]
public IHttpActionResult DeleteGetCarousel_Result(int id)
{
GetCarousel_Result getCarousel_Result = db.GetCarousel_Result.Find(id);
if (getCarousel_Result == null)
{
return NotFound();
}
db.GetCarousel_Result.Remove(getCarousel_Result);
db.SaveChanges();
return Ok(getCarousel_Result);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool GetCarousel_ResultExists(int id)
{
return db.GetCarousel_Result.Count(e => e.Id == id) > 0;
}
}
}
You should rename the class from GetCarousel to GetCarouselController as this is the convention for web api routing.
As a side note, it may also be preferable to rename it to something more appropriate such as "CarouselController", typically "GetCarousel" is much more appropriate as the name of an action, not a controller.