What should I include in my ASP.NET Core 6 MVC application's program.cs? - asp.net-core-mvc

I've migrated an ASP.NET MVC application to .NET 6 and notice that during testing, the arguments to an action method/controller uses query string rather than the usual REST-style URL.
For example, where the existing ASP.NET MVC 4 application would use the following style of URL
http://webserver/controller/action/123
the migrated web application uses
http://webserver/controller/action?id=123
I created the migrated app from scratch using the template for an ASP.NET Core 6 MVC application.
The program.cs is as follows:
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
// doesn't affect the app if it isn't hosted on IIS
builder.WebHost.UseIIS();
builder.WebHost.UseIISIntegration();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services
.AddControllers()
.AddMvcOptions(opts =>
{
opts.MaxModelBindingCollectionSize = 10000;
});
builder.Services
.AddRazorPages()
.AddRazorRuntimeCompilation();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});
try
{
Log.Information("Application starting up");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application startup failed");
}
finally
{
Log.CloseAndFlush();
}
}
The Home view renders links to the other controllers using Html helpers such as
#Html.ActionLink("Vendors", "Index", "VendorController", new { #Id = 15153, #Code = "AGR3" }, null)
and the VendorController action method is
[HttpGet]
public ActionResult Index(int id, string code)
{
// model is a POCO class
var model = _dataService.GetVendorInfo(id, code);
return View("Index", model);
}
How do I get my migrated ASP.NET Core MVC application to not use query string for passing arguments? Do I need to decorate the action method with some kind of routing attribute? I thought I could rely on convention-based routing as per the route pattern declared in the call to app.UseEndpoint in the Main() method of Program.cs?

In asp.net core,you could regist mutipule route patterns
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "myfirstroute",
pattern: "{controller}/{action}/{id?}/{Code?}");
app.MapControllerRoute(
name: "mysecondroute",
pattern: "{controller}/{action}/{id?}");
Specific the name of the route parttern with asp-route and add the required routedata as below(if your dictionary contains the key-values pairs whose key was not registed as a section in your route pattern,it would be added to the query part)
#{
var routedatadic = new Dictionary<string,string>();
routedatadic.Add("controller", "Vendor");
routedatadic.Add("action", "Index");
routedatadic.Add("id", "123");
routedatadic.Add("Code", "ABC");
}
<a asp-route="myfirstroute" asp-all-route-data="#routedatadic">Vendor1</a>
<a asp-route="mysecondroute" asp-all-route-data="#routedatadic">Vendor2</a>
The result:
You could check the document related

Related

Send Twilio a redirect Uri to another action in the same controller

I'm trying to send Twilio a redirect URI to another action in the same controller. I can't give a fully formed Uri because I'm in development and I'm using localhost. I thought I would use a relative Uri. The way it is written, I keep getting fed back in to "Welcome" when I'm trying to redirect to "RouteCall".
As a side note, routing in MVC seems very redundant. I couldn't get routing to work without explicitly using the Route tags that you see.
using System;
using Microsoft.AspNetCore.Mvc;
using Twilio.AspNet.Core;
using Twilio.TwiML;
namespace centurionvoice.Controllers
{
[Route("[controller]")]
[ApiController]
public class VoiceController : TwilioController
{
[Route("welcome")]
public IActionResult Welcome()
{
Uri newUri = new Uri("/RouteCall", UriKind.Relative);
var response = new VoiceResponse();
response.Say("Thank you for calling. To do some thing, press 1. To do another thing, press 0.");
response.Gather(numDigits: 1);
response.Redirect(newUri);
return TwiML(response);
}
[Route("routecall")]
[AcceptVerbs("GET", "POST")]
public IActionResult RouteCall(string digits)
{
var response = new VoiceResponse();
if (digits.Equals("1"))
{
//Dial the someone else
response.Say("You are being trasferred.");
return TwiML(response);
}
else
{
//Record a message
response.Say("Please record your message.");
response.Gather();
return TwiML(response);
}
}
}
}
The first thing I did was modify Startup.cs to configure basic/intuitive routing.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "api/{controller=Voice}/{action=Index}/{id?}");
});
}
Then back in VoiceController, new Uri() didn't throw errors anymore:
[HttpPost]
public IActionResult Index()
{
var response = new VoiceResponse();
var gather = new Gather(numDigits: 1, action: new Uri("/api/voice/gather", UriKind.Relative));
gather.Say("To do one thing, press 1. To do another thing, press 0.", voice: "Polly.Nicole");
response.Append(gather);
// If the user doesn't enter input, loop
response.Redirect(new Uri("/api/voice", UriKind.Relative));
return TwiML(response);
}

ASP.NET Core 3.1 MVC redirect in a custom AuthorizationHandler

In a ASP.NET Core 2 MVC app, I had a custom AuthorizationHandler that redirected blocked users back to the home page.
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsAllowedIpAddressRequirement requirement)
{
// Cast the context resource
if (context.Resource is AuthorizationFilterContext cxt)
{
// Failed!
cxt.Result = new RedirectToActionResult("Index", "Home", new { msg = "Your auth has failed." });
context.Succeed(requirement);
}
...
}
Since migrating to ASP.NET Core 3.1, the context is an object of class Microsoft.AspNetCore.Routing.RouteEndpoint, which has no Result property.
How can I redirect the user to a specific page?
I had the same problem and to solve it I changed to Filter (IAsyncResourceFilter) instead of Policy.
You can wrap your authorization logic into a policy and then invoke the IAuthorizationService and redirect anywhere/anytime you need.
Example:
public class MySampleActionFilter: IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//if failed
context.Result = new RedirectToRouteResult(new RouteValueDictonary(new
{
controller = "Your Controller",
action = "Your Action"
}));
}
}
By the way, this is for .net Core 3 and above
Documentation
if you want to user redirect to some page like login page, if user didn't has access, you could following below steps for fix it:
into HandleRequirementAsync method
if (Condition())
{
context.Succeed(requirement);
}
else {
context.Fail();
}
if user did has access, execute context.Succeed(requirement); and if user didn't has access, execute context.Fail();
into startup.cs => ConfigureServices method
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromHours(12);
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
options.SlidingExpiration = true;
});
in line that we write
options.LoginPath = "/Account/Login";
we appointment users after failing in HandleRequirementAsync method for checking access, being redirected to controller 'home' controller and 'login' actiion.
i'll hope my answer be useful for friends.

Cannot implement multiple GET methods in WebApi OData

I'm using OData V3 with MVC4 Web API project .NET4.
The WebAPI register method is:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.None;
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<ClientModel>("ODClient");
builder.ComplexType<ClientStatus>();
builder.ComplexType<ClientType>();
var edmmodel = builder.GetEdmModel();
config.Routes.MapODataRoute(
routeName: "odata",
routePrefix: "odata",
model: edmmodel
);
}
The OData controller is:
[HttpGet]
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All, PageSize = 25)]
public IQueryable<ClientModel> Get()
{
var model = ...
return model;
}
[HttpGet]
public ClientModel Get([FromODataUri] int id)
{
return new ClientModel();
}
[HttpDelete]
public void Delete([FromODataUri] int id)
{
}
This query runs well:
http://localhost:59661/odata/ODClient?$filter=id eq 3
But this query doesn't work:
http://localhost:59661/odata/ODClient(3)
It executes first GET query with all items.
The Delete doesn't work either (the request type is DELETE):
http://localhost:59661/odata/ODClient(3)
The error received is:
"No HTTP resource was found that matches the request URI 'http://localhost:59661/odata/ODClient(12)'."
As per question comments, the issue was the way that the default routing conventions assigns a name to parameters. Keys are actually given the default name of "key" and so switching to that worked.
The name can be customized by either creating a custom routing convention that populates the route data table with a "id" value, or by using attribute based routing in which case the parameter name can match the name specified in the path template.

Sitecore context not loaded in custom controller

I followed this tutorial, and created this code:
using Glass.Sitecore.Mapper;
using Sitecore.Mvc.Controllers;
using Sitecore.SecurityModel;
using SitecoreCMSMVCBase.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace SitecoreCMSMVCBase.Controllers
{
public class CommentController : SitecoreController
{
ISitecoreContext _context;
ISitecoreService _master;
public CommentController()
: this(
new SitecoreContext(),
new SitecoreService("master"))
{
}
/// <summary>
/// This constructor can be used with dependency injection or unit testing
/// </summary>
public CommentController(ISitecoreContext context, ISitecoreService master)
{
_context = context;
_master = master;
}
[HttpGet]
public override ActionResult Index()
{
var model = _context.GetCurrentItem<CommentPage>();
return View(model);
}
[HttpPost]
public ActionResult Index(Comment comment)
{
var webModel = _context.GetCurrentItem<CommentPage>();
if (ModelState.IsValid)
{
var masterModel = _master.GetItem<CommentPage>(webModel.Id);
if (masterModel.CommentFolder == null)
{
CommentFolder folder = new CommentFolder();
folder.Name = "Comments";
using (new SecurityDisabler())
{
_context.Create(masterModel, folder);
}
masterModel.CommentFolder = folder;
}
using (new SecurityDisabler())
{
comment.Name = DateTime.Now.ToString("yyyyMMddhhmmss");
//create the comment in the master database
_master.Create(masterModel.CommentFolder, comment);
webModel.CommentAdded = true;
}
}
return View(webModel);
}
}
}
Models are identical with tutorial, so I will not paste them.
My route configuration looks like this:
routes.MapRoute(
"CommentController", // Route name
"Comment/{action}/{id}", // URL with parameters
new { controller = "Comment", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
When I navigate to /comment I see this exception:
Glass.Sitecore.Mapper.MapperException: Context has not been loaded
I tried with commenting my route specification (as there was nothing about routes in tutorial), and then error is different (throwing by Sitecore CMS itself):
The requested document was not found
Do you know how to load Sitecore context into custom Controller, and make this simple example work? I was looking everywhere but couldn't find any good answer...
I think this is more a Glass setup issue, rather than an MVC routing problem.
To setup Glass, you need to initialise the context in your application start method in your Global.asax file.
var loader = new Glass.Sitecore.Mapper.Configuration.Attributes.AttributeConfigurationLoader(
"Glass.Sitecore.Mapper.Tutorial.Models, Glass.Sitecore.Mapper.Tutorial");
Glass.Sitecore.Mapper.Context context = new Context(loader);
For other Glass-setup related stuff I recommend following the first tutorial on the glass.lu website.
http://www.glass.lu/tutorials/glass-sitecore-mapper-tutorials/tutorial-1-setup/
This method doesn't need Glass at all!
First step is to set your route in Global.asax file.
routes.MapRoute(
"DemoController", // Route name
"Demo/{action}/{param}", // URL with parameters
new { controller = "Demo", action = "Index", param = "", scItemPath = "/sitecore/content/DemoHomePage" } // Parameter defaults
);
Notice that controller is not taken as parameter, but is fixed, to prevent handling it by Sitecore. More info here and here. Notice that there is one additional parameter - scItemPath. It contains path to item which by default will be included in page context.
Having this route our traffic from /demo is handled by DemoController and Index action. Inside this action all you need is to add is this line:
Sitecore.Data.Items.Item item = Sitecore.Mvc.Presentation.PageContext.Current.Item;
item variable will contain your Sitecore item pointed by scItemPath.
And that's all - it should work well now - hope it helps!

How to add different MediaTypeFormatters for different controllers

In the new ASP.NET Web Api you can hookup MediaTypeformatters to a controller like this:
protected void Application_Start()
{
var config = GlobalConfiguration.Configuration;
config.Formatters.Add(new ContactPngFormatter());
config.Formatters.Add(new VCardFormatter());
config.Routes.MapHttpRoute(
"Default", // Route name
"{controller}/{id}/{ext}", // URL with parameters
new { id = RouteParameter.Optional, ext = RouteParameter.Optional }
);
}
But this is a "global" configuration. What if I want a different set of MediaTypeFormatters for a specific controller. How would I create a specific configuration for that?

Resources