ASP.Net Core Web API convention-based routing? - c#
What am I missing that I'm greeted with a 404 for this controller? I really don't want to use attribute-based routing. I also don't want action to be part of any URIs.
I'm using Visual Studio 2017 and .Net Core 1.1.
TestController.cs
using System;
using Microsoft.AspNetCore.Mvc;
namespace Foo.Controllers
{
public class TestController : Controller
{
public long Get() => DateTimeOffset.Now.ToUnixTimeSeconds();
}
}
Note that this works with a [Route("api/Test")] attribute. But I don't want to use attribute-based routing. And as soon as I take that attribute off, I get 404s.
Startup.cs
namespace Foo
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{controller}/{id?}"
);
});
}
}
}
Note there's also some stuff in here for Autofac/DI, but I took that out to remove the distraction.
Debug output from a request
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3308908Z","tags":{"ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.operation.id":"0HL37O0HBESDL","ai.application.ver":"1.0.0.0"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request starting HTTP/1.1 GET http://localhost:50129/api/test","severityLevel":"Information","properties":{"DeveloperMode":"true","Host":"localhost:50129","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","Path":"/api/test","Protocol":"HTTP/1.1","Method":"GET","Scheme":"http"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:50129/api/test
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3633954Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request successfully matched the route with name 'default' and template 'api/{controller}/{id?}'.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Routing.RouteBase","RouteName":"default","{OriginalFormat}":"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.","RouteTemplate":"api/{controller}/{id?}"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3663952Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"No actions matched the current request","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler","{OriginalFormat}":"No actions matched the current request"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3693962Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request did not match any routes.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Builder.RouterMiddleware","{OriginalFormat}":"Request did not match any routes."}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3753962Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Connection id \"0HL37O0H95P8K\" completed keep alive response.","severityLevel":"Verbose","properties":{"DeveloperMode":"true","ConnectionId":"0HL37O0H95P8K","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Server.Kestrel","{OriginalFormat}":"Connection id \"{ConnectionId}\" completed keep alive response."}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-03-10T14:18:01.3878990Z","tags":{"ai.cloud.roleInstance":"Desktop","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.location.ip":"::1","ai.operation.id":"0HL37O0HBESDM","ai.application.ver":"1.0.0.0","ai.internal.nodeName":"Desktop","ai.operation.name":"GET /api/test"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request finished in 54.7982ms 404","severityLevel":"Information","properties":{"DeveloperMode":"true","ElapsedMilliseconds":"54.7982","AspNetCoreEnvironment":"Development","CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","StatusCode":"404"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 54.7982ms 404
This is not works, as mapping to action method is not defined. AFAIK, you may achieve the WebApi REST like routing ONLY using the attribute routing and you may define it on controller level:
[Route("api/[controller]")]
public class TestController : Controller
{
[HttpGet]
public long Get() => DateTimeOffset.Now.ToUnixTimeSeconds();
}
Update: have found this github issue Web API not working with convention based routin and:
for ASP.NET Core MVC we decided to adopt MVC 5.x's conventional routing approach and not Web API 2.x's approach. With the conventional routing approach, the route must specify both a controller and an action.
You may change route template to
template: "api/{controller}/{action}/{id?}"
But in this case, your URL will be /api/test/get.
Update 2 (based on the guide): you can include the NuGet package for Microsoft.AspNetCore.Mvc.WebApiCompatShim and still use ApiController. The code is on GitHub if you are curious as to what it does. Then you can define the WebApi routing:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMvc(routes =>
{
routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
}
Related
Simple .Net 5.0 web app getting 404s (using mvc)
.Net novice here. I am following along with a web development bootcamp and I have had a persistent issue for the last few days. I've ran it by several TA's, other students, and a few instructors but we have all been stumped thus far. I am getting 404s when opening the localhost:5000 page in my browser or using postman. The project was generated using the dotnet new web --no-https -o ProjName command and then edited. Here is what my Startup looks like: public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => options.EnableEndpointRouting = false); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); } } Here is what my controller looks like using Microsoft.AspNetCore.Mvc; namespace Portfolio.Controllers { class HomeController : Controller { [HttpGet("")] public string Index() => "This is my index"; } } Note, I receive a response when using the default auto generated code but not using this Controller with attribute routing pattern. If you know anything about how to resolve this it would be a great help as I am falling behind. Things I have tried: Uninstalling everything related to .net and installing a single version (5.0.100 currently, but I tried a few others as well) Uninstalling and reinstalling IIS Express Trying to run the project from another computer. No luck. Changing the port. No luck. Using a different browser. No luck. Sobbing on my keyboard. No luck. Remaking the same project a dozen times. I am happy to provide any additional information and I am generally tech savvy enough to follow up on what is suggested. Thank you
Specifying an empty string in the HttpGet attribute is like specifying none. You can define a default route pattern in the useMvcmethod: app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); }); Or set a slash in the HttpGet attribute to set the root path for the method: HttpGet("/")
Can I have MVC Application Controller classes for AJAX API calls in a APS.NET Core Web Application project?
I created a new .NET Core application project targeting 2.1 By default it uses page routing, so new cshtml files I create follow standard file page routing. Next I wanted to test making ajax calls from the client to my server. I attempted to add MCV application Controller called TestController and added the following to it: public class TestController : Controller { // GET: /<controller>/ public IActionResult Index() { return View(); } [HttpPost("Test/RunTest")] public ActionResult<string> RunTest() { return "Got it!"; } } Here is my startup.cs file: public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(); } When I attempt to make a Post via Postman to https://localhost:44313/Test/RunTest , I get a 404 page not found error. I think it because my razor project is not routing requests to the controllers in my project, but I am not sure. What should I do to make ajax request to MVC controllers ?
The attribute route template on your Runtest action doesn't have a prefix specified, so you shouldn't prefix the URL with "/Test/". The AJAX post should go to https://localhost:44313/RunTest. If you want the controller name as part of the route, you can include it in the template: [HttpPost("Test/RunTest")] Or you can specify a prefix for all actions in the controller: [RoutePrefix("Test")] public class TestController : Controller
What are the differences between ConfigureServices() and Configure() in ASP.NET Core?
The documentation on learn.microsoft.com states the following: Use ConfigureServices method to add services to the container. Use Configure method to configure the HTTP request pipeline. Can someone explain with simple examples, what is meant by adding services to container and what is meant by configuring HTTP request pipeline?
In a nutshell: ConfigureServices is used to configure Dependency Injection public void ConfigureServices(IServiceCollection services) { // register MVC services services.AddMvc(); // register configuration services.Configure<AppConfiguration>(Configuration.GetSection("RestCalls")); // register custom services services.AddScoped<IUserService, UserService>(); ... } Configure is used to set up middlewares, routing rules, etc public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // configure middlewares app.UseMiddleware<RequestResponseLoggingMiddleware>(); app.UseMiddleware<ExceptionHandleMiddleware>(); app.UseStaticFiles(); // setup routing app.UseMvc(routes => { routes.MapRoute( name: "Default", template: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = 1 }); }); } Read ASP.NET Core fundamentals to understand how it works in details.
Items in ConfigureServices are part of Dependency Injection like logger, Database etc. These kind of things aren't directly associated with a http request. Items in configure are part of a http request like routing, mididlewares, static files all these triggers directly when user makes a request.
ASP.Net Core 1.1 - Angular2 project CSP not working
At the moment I'm using NWebsec.AspNetCore.Middleware nuget package to implement CSP headers. When simply creating a new ASP.NET Core 1.1 MVC Application and modifying the startup as followed, it's still possible to inject a script via the console. What am I missing? public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); app.UseCsp(csp => { csp.DefaultSources(src => src.Self()); }); } } The intension is to set CSP security to Angular2 generated static files (WebPack). But the CSP does not seem to be applied.
Middleware runs from top to bottom, this means that if a middleware exits early, then middlewares that are registered later in the pipeline will not run. For example, you usually configure the static files middleware before the MVC middleware. That way, the application will first attempt to find a static file matching the current request and serve that directly (which will skip MVC). Only when there is no file, it will fall back to the MVC route lookup. This ultimately means that everything that is there to secure stuff, or to lock users out of completing requests with “real” middleware (those that actually return stuff), has to be registered at the beginning of the pipeline. In your case, you need to make sure that you call UseCsp() before UseMvc to make sure that the CSP middleware runs before the MVC middleware.
Troubleshooting a ridiculously simple WebApi in VS Code on NET.Core
I have a working webpage (front-end strictly) and I was curious of I could add a WebApi without leaving VS Code. So I created a directory called webapi in the root of my project and added a file demo.cs containing the following. using Microsoft.AspNetCore.Mvc; namespace WebApi { [Route("api/[controller]")] public class Demo : Controller { [HttpGet] public string Ping() { return "yo!"; } } } After some googlearching for references, I made sure that my project.json contains the following. "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", "Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.AspNetCore.Mvc.Core": "1.0.1", I also edited the configuration methods like this. public void ConfigureServices(IServiceCollection services) { services.AddMvcCore(); services.AddDirectoryBrowser(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseMvc(); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseFileServer(true); app.UseMvcWithDefaultRoute(); } Now, as I execute the project with dotnet run, I get no errors, the page functions but I can't seem to access the text I meant to expose. The extra problem's that I'm not sure if I've got the WebApi running but using the wrong URL (I went localhost:port with /api/donkey) or if it's not running at all. How can I verify that it's up? What would be the address? Is there something else I'm missing in the setup? The guides tell partially different things, which I guess depends on the rapid evolution of NET.Core. Not sure how to proceed.
Using documentation from Routing to Controller Actions To make attribute routing less repetitive, route attributes on the controller are combined with route attributes on the individual actions. Any route templates defined on the controller are prepended to route templates on the actions. Placing a route attribute on the controller makes all actions in the controller use attribute routing. using Microsoft.AspNetCore.Mvc; namespace WebApi { [Route("api/[controller]")] public class Demo : Controller { [HttpGet] // Matches 'GET /api/Demo' public string Ping() { return "yo!"; } } } the start up should have this in the configure services // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); //...other code }