im currently trying to create a Controller in ASP.net core mvc with an optional parameter "id".
Im fairly new to asp.net, I've tried to check other posts but nothing solved my problem.
public class TestController : Controller
{
private readonly ITestRepository _TestRepository;
public TestController(ITestRepository TestRepository)
{
_TestRepository = TestRepository;
}
public async Task<IActionResult> Index(string id)
{
if (string.IsNullOrEmpty(id))
{
return View("Search");
}
var lieferscheinInfo = await _TestRepository.GetTestdata(Convert.ToInt64(id));
if (lieferscheinInfo == null)
{
// return err view
throw new Exception("error todo");
}
return View(lieferscheinInfo);
}
}
I want to open the site like this "localhost:6002/Test" or "localhost:6002/Test/123750349" e.g, the parameter can be an int as well, i've tried both(string and int) but it doesnt work.
Either the site returns 404 (for both cases, with and without an parameter) or the parameter gets ignored and is always null.
I've tried to add [Route("{id?}")] on the Index but it did not change anything.
Greetings
your code should work just fine. string parameters accepts null by default so you dont need to specify can you check how the routing is set up in your startup.cs file.
You can add routing through attribute for example the following :
[Route("Test")]
[Route("Test/Index")]
[Route("Test/Index/{id?}")]
Will allow you to access the Action using :
/test/?id=sasasa
test/Index/?id=sasasa
test/Index
check Routing Documentation at Microsoft site :
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-3.1
In your project's Startup class make sure you are using the right MapControllerRoute
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Test}/{action=Index}/{id?}");
});
Related
EDIT: If I create an empty ASP.NET CORE WEB APP MVC, I can make it working. I am having problem when I am using MVC with Angular. There might be a problem with SPA proxy as well.
EDIT 2: I found a report https://github.com/dotnet/aspnetcore/issues/38354
I am still trying but no chance.
I can not access my public methods in controller classes. This is my controller:
[Route("authentication")]
public class AuthenticationController : Controller
{
[HttpGet("example")]
public IActionResult Example()
{
return Ok("This is the Welcome action method...");
}
}
And also I tried this attribute as well:
[Route("[controller]")]
public class AuthenticationController : Controller
when I try to navigate to localhost:PORT/authentication/example I am getting 404. I am not using API. I am trying to build a web application with .net core MVC and angular. So I will be just sending GET or POST requests to controllers.
This is my program.cs file
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.Run();
I strongly believe that something is wrong in my program.cs. But I couldn't figure it out.
FIX:
After trying out a few days, I finally found the answer. I had to add my new route into 'proxy' variable in proxy.conf.js file.
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
"/authentication"
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}}
]
you can try this for example, it will work for localhost:PORT/authentication/example
[Route("[controller]/[action]")]
public class AuthenticationController : Controller
{
public IActionResult Example()
{
return Ok("This is the Welcome action method...");
}
}
//or
public class AuthenticationController : Controller
{
[HttpGet("~/Authentication/Example")]
public IActionResult Example()
{
return Ok("This is the Welcome action method...");
}
}
but since you are using a Controller as a base class, not an ApiController for example, everything should be working even if you remove all attribute routing at all.
You need to decorate your controller with method / routing attributes
Try:
[Route("api/[controller]")]
public class AuthenticationController : Controller
{
[HttpGet("example")]
public IActionResult Example()
{
return Ok("This is the Welcome action method...");
}
}
This will create a get endpoint which can be called at api/authentication/example
Returning a 200 status with the text in the body.
The convention is that if Your memers start with an action verb, it can find out automatically, like
public string GetExample()
However you do not want to return raw string, you always want to return an action result, because you want wrapping with explicit HttpStatus response codes, so
public IActionResult<string> GetExample()
Now many of us a bias towards the works by magic because of prefix and like to be more explicit, not only because the attribute notation allows more control, but also for consistency. Because nearly almost always, at least one action method of the controller actually requires that fine grain.
[HttpGet("example")]
public IActionResult<string> Example()
Then often for instance there is an id and you can go
[HttpGet("example/id?")]
public IActionResult<string> Example([FromRoute] string id)
if you want to not have it go through all the places it might be getting your variables from for instance, there are many choices available
I can access Action when I go over the link, but when I do RedirectToAction, I get an error "No route matches the supplied values.".
When I use "orders/list" route. I come to List action but I cannot come to Index action after redirect.
OrdersController.cs
public class OrdersController : Controller
{
public IActionResult List()
{
return RedirectToAction("Index","Orders");
}
public IActionResult Index()
{
return View();
}
}
TranslationTransformer.cs
public class TranslationTransformer : DynamicRouteValueTransformer
{
private readonly TranslationDatabase _translationDatabase;
public TranslationTransformer(TranslationDatabase translationDatabase)
{
_translationDatabase = translationDatabase;
}
public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
return values;
}
}
Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapDynamicControllerRoute<TranslationTransformer>("{controller}/{action}");
});
You can change
return RedirectToAction("Index","Orders");
to
return Redirect("/Orders/Index");
Here is a working demo:
Controller:
public class OrdersController : Controller
{
public IActionResult List()
{
return Redirect("/Orders/Index");
}
public IActionResult Index()
{
return View();
}
}
result:
I encountered this issue today, and what seems to have fixed it (so far) in my project is adding a second endpoint in Startup.Configure.
app.UseEndpoints(endpoints =>
{
string defaultPattern = "{controller=Home}/{action=Index}/{id?}";
endpoints.MapDynamicControllerRoute<MyTranslationTransformer>(defaultPattern);
// This is the second endpoint I'm referring to:
endpoints.MapControllerRoute(
name: "default",
pattern: defaultPattern);
});
I'm not certain exactly why this is working. I tried it after I read this GitHub issue, noticed multiple endpoints in the examples, and tried the one that was present in the original project template generated by Visual Studio.
My current thought is basically just "Hmmm, need to supply a default route it seems." However, why it appears to only affect RedirectToAction confuses me. Perhaps there are other methods affected that I haven't noticed which might shed light on the underlying mechanism.
My controller is
public partial class GridController : Controller
{
[Route("/grid/{name}")]
public IActionResult Index(string name)
{
}
}
Routing is setup correctly since if i visit /grid/something i get http ok.
But how can i set a default parameter in startup.cs?
I tried the following but on page load, i get http 404 error
endpoints.MapControllerRoute(
"default",
"{controller=Grid}/{action=Index}/{name}");
Okay. Ended up with a workaround
endpoints.MapControllerRoute(
"default",
"{controller=Grid}/{action=Index}");
and by adding an extra action overload.
public IActionResult Index()
{
return Index("something");
}
it simply works
[Route("/grid/{name}")]
public IActionResult Index(string name )
{
}
Use attribute routing at Controller level, So that it will be applicable to all the action method under that controller.
In your route template. The name parameter is not optional.
So, use {name?}.
First of all, you did not specify the name parameter in your endpoint configuration. So it should have been {controller=Grid}/{action=Index}/{name=something}. But if there are overloads, this does not seem to work anyway. I wonder if this is a bug or is it intended by design. It seems that if there is are multiple overloads of the same action, execution gets directed to the parameterless one.
Here's another workaround with the possibility to use parameters. I tried this with .NET Core 2.2. Create a new action with the same parameters, but without RouteAttribute. From there you can do a redirect to the necessary action. Here's an example where I use TestIndex for the default route.
[Route("/[controller]/{name}/{age}")]
public IActionResult Index(string name, int age)
{
// implementation
return Ok($"{name} is {age} years old.");
}
public IActionResult Index()
{
return Index("Mr. It", 7);
}
public IActionResult TestIndex(string name, int age)
{
return RedirectToAction(nameof(Index), new { name, age });
}
Then for default route use the new method, e.g. {controller=Grid}/{action=TestIndex}/{name=That}/{age=42}.
If you use this for testing, the new action could be omitted from production by adding a TypeFilterAttribute. See this answer for more details.
I'm struggling with this for some time now. I've searched all over the internet, didn't find any solution.
I'd like to create a webapi project with somewhat custom routing. Using VS 2019, project is of type ASP.NET WebApi on .NET Core 2.2. Routing should be like this:
Basic application must reside on url similar to "https://my.server.com/myapi". URLs which will be called are in form "https://my.server.com/myapi/{InstanceName}/{CommandName}?{customParams}"
I have one controller defined in my project and I would like to redirect all requests to that controller, where instanceName could be parameter of all the methods contained in a controller, so I would get a value for that parameter. CommandName is basicly the same as "action" RouteData by MVC principles. As you can see there is no controller specified, since all is handled by one controller.
So far I've tried setup routing like this:
Startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "MyRoute",
template: "{instance}/{action}",
defaults: new { controller = "MyController" });
});
}
MyController.cs
[Route("/")]
[ApiController]
public class MyController : ControllerBase
{
[HttpGet("{instance}/info")]
public JsonResult Info(string instance, InfoCommand model)
{
// Just return serialized model for now.
var result = new JsonResult(model);
return result;
}
}
But this does not work. I get 415 response from (I think) web server when I call for example
https://my.server.com/myapi/MYINSTANCE/info?param1=value1¶m2=value2
While debugging from VS this URL looks like this:
https://localhost:12345/MYINSTANCE/info?param1=value1¶m2=value2
but I think it shouldn't matter for routing.
In best case scenario (putting [Route("{instance}")] above controller and [HttpGet("info")] above Info method) I get 404 response, which is also what I do not want.
I've even tried creating my own ControllerFactory, but that didn't work either (changing controller inside ControllerFactory's create method and adding another parameter to RouteData).
How to setup routing like that? Is it even possible? I would still like to use all other MVC features (model binding, proper routing, auth features, etc.), it's just this routing I cannot figure it out.
Your attempt resulting a in 415 Unsupported Media Type error was your best one.
You were only missing the FromQuery as shown below.
The error indicates that the complex type InfoCommand could not be resolved.
You must specify that it must be parsed from the querystring.
Note that the route defined via MapRoute doesn't have effect, since you are using attribute-based routing; it's only one or the other.
[Route("/")]
[ApiController]
public class MyController : ControllerBase
{
[HttpGet("{instance}/info")]
public JsonResult Info(string instance, [FromQuery] InfoCommand model)
{
var result = new JsonResult(model);
return result;
}
}
public class InfoCommand
{
public InfoCommand()
{}
public string Param1 { get; set; }
public string Param2 { get; set; }
}
First, a little disclaimer: I have already created a GitHub issue for this at the aspnet-api-versioning repo. The content of this question is basically the same as the content in that github issue.
I am using ASP.NET Web API on .NET 4.5.2.
My example API looks like this:
namespace App.Backend.Controllers.Version1
{
[ApiVersion("1.0")]
public class SomeController: ApiController
{
[HttpGet]
[Route("api/entity/{id:int:min(1)}")]
public async Task<IHttpActionResult> ApiAction(int id) //Already running in production
{
//Accessible by using ?api-version=1.0 OR by omitting that since this is the default version
return Ok();
}
}
}
namespace App.Backend.Controllers.Version2
{
[ApiVersion("2.0")]
public class SomeController : ApiController
{
[HttpGet]
[Route("api/entity/{id:int:min(1)}")]
public async Task<IHttpActionResult> ApiAction(int id)
{
//Accessible by using ?api-version=2.0 OR by omitting that since this is the default version
return Ok();
}
}
}
The config is as follows:
// Add versioning
config.AddApiVersioning(o =>
{
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
When I send a request, though, the following happens:
System.InvalidOperationException: A route named 'RegisterHours' is already in the route collection. Route names must be unique.
Duplicates:
api/some/ApiAction
api/some/ApiAction
This is weird to me because in the wiki there is an example exactly like my situation
I'd like to use the ?api-version={version} option but it looks I have no choice to use the URL path version now (api/v1.0/some/apiAction and api/v2.0/some/apiAction. If that's true, I guess I have to add another Route to every existing action which will be like api/v{version:apiVersion}/controller/action to allow them to use v1.0 so it will be uniform in the entire application?
What do you guys advise? I could just use /v2.0/ in the URL of version 2 of the API I guess, but I'd prefer the query string version.
It doesn't show it in the example, but the error message:
System.InvalidOperationException: A route named 'RegisterHours' is already in the route collection. Route names must be unique.
means that there are multiple entries with the same name in the route table. If I had to guess, the attribute routes are actually being defined as:
[Route("api/entity/{id:int:min(1)}", Name = "RegisterHours")]
...or something like that.
Unfortunately, the route table is not API version-aware. The names in the route table must be unique. When you specify the name in the RouteAttribute, it causes this issue. The only real way around it is to use unique route names; for example, Name = "RegisterHoursV1" and Name = "RegisterHoursV2".
Aside: you don't need:
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap = { ["apiVersion"] = typeof( ApiVersionRouteConstraint ) }
};
configuration.MapHttpAttributeRoutes( constraintResolver );
unless you are versioning by URL segment.
After you have fixed the duplicate "RegisterHours" (as per the git hub issues page responses) you should also ensure you have the constraintResolver setup in your startup.cs
public void Configuration( IAppBuilder builder )
{
// we only need to change the default constraint resolver for services that want urls with versioning like: ~/v{version}/{controller}
var constraintResolver = new DefaultInlineConstraintResolver() { ConstraintMap = { ["apiVersion"] = typeof( ApiVersionRouteConstraint ) } };
var configuration = new HttpConfiguration();
var httpServer = new HttpServer( configuration );
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
configuration.AddApiVersioning( o => o.ReportApiVersions = true );
configuration.MapHttpAttributeRoutes( constraintResolver );
builder.UseWebApi( httpServer );
}
Otherwise your attributes to change the route (api/entity) won't work because the route doesn't match the controller name "Some" and so won't match the default routing ie. ~\api\controllername\
public class ***Some***Controller : ApiController
{
[HttpGet]
[Route( "api/***entity***/{id:int:min(1)}" )] <--- won't work without the constraint resolver
public async Task<IHttpActionResult> ApiAction( int id )
{
//Accessible by using ?api-version=2.0 OR by omitting that since this is the default version
return Ok();
}