How to override a route in MVC 5 - asp.net-mvc-5

I've two controller controllerOne is defined under an area AreaOne . I need to reroute one of the action defined in the controllerOne to the action defined in controllerTwo I tried attribute routing but its not working. my current code is given below
Action In Controller One
public class ControllerOne
{ public ActionResult CustomerSearch()
{
return View("Search", model);
}
}
Action In Controller Two
[Route("AreaOne/ControllerOne/CustomerSearch")]
public class ControllerTwo
{ public ActionResult CustomCustomerSearch()
{
return View("Search", model);
}
}
How can I achieve this.?

In RouteConfig.cs you can use routes.MapRoute to associate your URL with any controller & action in your project.
routes.MapRoute(
name: "OverrideCustomerSearch",
url: "AreaOne/ControllerOne/CustomerSearch",
defaults: new { controller = "Two", action = "CustomCustomerSearch" }
);

Related

ASP.Net MVC: Routing issue which throwing exception

I have a test controller where i have one index action which accept custid as a argument.
this is how my controller looks
public class TestController : Controller
{
// GET: Test
public ActionResult Index(int custid)
{
return View();
}
}
i have added one extra routing statement in route.config file.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "custom1",
url: "{controller}/{id}",
defaults: new { controller = "Test", action = "Index", custid = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
so when accessing test controller action with url like localhost:50675/test/101 then getting error. error message say
The parameters dictionary contains a null entry for parameter 'custid'
of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Index(Int32)' in
'WebAPICRUD.Controllers.TestController'. An optional parameter must be
a reference type, a nullable type, or be declared as an optional
parameter. Parameter name: parameters
but when accessing test controller action with url like localhost:50675/test?custid=101 then getting no error.
so i do not understand what mistake is there in code.
What i need to do as a result i can issue this url http://localhost:50675/test/101 which should work. please guide me. thanks
Your route definition need to contain a segment for custid (not id) to match the name of the parameter. The route definition should also contain the name of the controller to make it unique
routes.MapRoute(
name: "custom1",
url: "Test/{custid}", // modify
defaults: new { controller = "Test", action = "Index"}
);
Note that you can also remove the custid = UrlParameter.Optional since you do not want it to be optional

Load catalogs from external DB and link them to ContentParts as ContentRecords

Example: I have a countries catalog stored in another DB and I need to use it as a property in some ContentParts. I'm trying to make the connection without interfering much with Orchard wiring.
public class MoviePart : ContentPart<MoviePartRecord>
{
public IEnumerable<CountryRecord> Countries
{
get
{
return Record.Countries.Select(r => r.CountryRecord);
}
}
}
The relation between CountryRecords and MovieParts will be on the Orchard DB, but the CountryRecord data is in another DB. I only need Read access, but I don't get which and how to override the Handler to use the other source.
Do I need to create a ContentHandler and override all methods, and create another StorageFilter that uses the new repository with the external source? And how would I inject the new repo into the handler?
public class CountryPartHandler : ContentHandler
{
public CountryPartHandler(IRepository<CountryPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
protected override void Loading(LoadContentContext context)
{
base.Loading(context);
}
}
Update:
In this Using External Data with Orchard (around 25th min) video, he seems to be doing what I need with this code:
public ProductPartHandler(IRepository<ProductPartRecord> repository, Work<IProductService> productServiceWork)
{
Filters.Add(StorageFilter.For(repository));
OnActivated<ProductPart>((context, part) => {
part.ProductField.Loader(() => productServiceWork.Value.GetProduct(part.Id));
});
}
But in my code it can't find the "Loader" function, even though I have all the references from the video too, so maybe ProductField is a custom type?
So that is a lazy field on the part, something like this:
public class MyPart : ContentPart {
internal readonly LazyField<CustomData> CustomDataField = new LazyField<CustomData>();
public CustomData CustomData {
get { return CustomDataField.Value; }
}
}
public class CustomData {
...
}
public class MyPartHandler : ContentPartHandler {
private ICustomService _customService;
public MyPartHandler(ICustomService customService){
_customService = customService;
OnActivated<MyPart>(Initialize);
}
private void Initialize(ActivatedContentContext context, MyPart part){
part.CustomDataField.Loader(() => {
return _customService.Get(part.ContentItem.Id);
});
}
}
I don't know how you are loading your external data, whether via rest, wcf etc., but the logic can just be thrown into the custom service

How to pass activity to formbuilder class in Bot Framework

I have below LUIS Dialog class from which I redirect it to formbuilder class, I want to pass a context.Activity to my formbuilder method which and it resides in another class is given right after the code.How to do this?
public class LUISManager : LuisDialog<object>
{
[LuisIntent("BookFlight")]
public async Task BookFlight(IDialogContext context, LuisResult result)
{
//context.Activity
FlightBooking cb = new FlightBooking();
BuildFormDelegate<FlightBooking> MakeFlightBookingForm = FlightBooking.BuildForm;
var flightBooking = new FormDialog<FlightBooking>(cb, MakeFlightBookingForm, FormOptions.PromptInStart, null);
context.Call<FlightBooking>(flightBooking, FlightBookingComplete);
}
}
[Serializable]
public class FlightBooking
{
public static IForm<FlightBooking> BuildForm()
{
return new FormBuilder<FlightBooking>().Message("Tell me meeting details!")
.Field(nameof(title))
.Field(nameof(StartDate))//, validate: ValidateStartDate
.Field(nameof(EntryTime), validate:ValidateCallTime)
.Build();
}
},
I know we can pass "IDialogContext context" parameter in BuildForm method but how to pass it from BuildFormDelegate
Change the constructor to:
public static IForm<FlightBooking> BuildForm(IDialogContext context)
{}
Then you can use a lambda expression:
BuildFormDelegate<FlightBooking> MakeFlightBookingForm = ()=>FlightBooking.BuildForm(context);

MVC AttributeRoute appears to be ignoring RoutePrefix and causing matching action in mutiple Controllers error

I am using the MVC Attribute Routing (MVC 5.1.2) and am running into the error:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
FFInfo.WebUI.Areas.Admin.Controllers.HomeController
FFInfo.WebUI.Areas.Admin.Controllers.SectionController
This only happens when I go to /Admin/Sections/ and I am not really sure why since there is only one route that can match that URL, can anyone help me figure out what is wrong? Please note this problem is unique to 5.1.2, MVC 5.0 it works fine.
Base Controller:
[RouteArea("Admin")]
public class BaseController : Controller
{
}
Home Controller:
[RoutePrefix("")]
[Route("{action}")]
public class HomeController : BaseController
{
public ActionResult Index()
{
}
public ActionResult Updates()
{
}
[ChildActionOnly]
public PartialViewResult GetUpdatesGrid()
{
}
public ActionResult GetUpdates(JqGridRequest Request)
{
}
}
Section Controller:
[RoutePrefix("Sections")]
[Route("{action}")]
public class SectionController : BaseController
{
[Route]
public ActionResult Sections()
{
}
[ChildActionOnly]
public PartialViewResult GetSectionsGrid()
{
}
public ActionResult GetSections(JqGridRequest Request)
{
}
public ActionResult AddSection()
{
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult AddSection(AddEditSectionVM model, HttpPostedFileBase LogoFile)
{
}
public ActionResult EditSection(Int16? ID)
{
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult EditSection(AddEditSectionVM model, HttpPostedFileBase Logo)
{
}
public ActionResult Releases()
{
}
[ChildActionOnly]
public PartialViewResult GetReleasesGrid()
{
}
public ActionResult GetReleases(JqGridRequest Request)
{
}
public ActionResult AddRelease()
{
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult AddRelease(AddEditReleaseVM model)
{
}
}
My understanding of the RouteArea RoutePrefix, and Route attributes tells me that /Admin/Index will call the Index ActionResult of the Home Controller and the URL Admin/Sections should call the Index ActionResult of the Sections Controller. All the other routes work perfectly fine in each controller and when you go to /Admin/Index that works fine. I only get this error when I go to /Admin/Sections. What is wrong?
This appears to be a side-effect of a breaking change in ASP.Net MVC 5.1 related to how Attribute Routing handles potential ambiguous matches:
http://www.asp.net/mvc/overview/releases/mvc51-release-notes
We ran into similar issues when updating from 5.0 to the current 5.1.2. It seems like nested routes like this just happened to be working based on the old logic, and now they fail due to the strict breaking change.
In your example, /Admin/Index could technically match on the HomeController since it could be interpreted as /{area=Admin}/{action=Index}. It doesn't seem like there is any special logic (or at least, there doesn't seem to be anymore) that looks to see if the {action} segment happens to match a defined RoutePrefix on an alternate controller in the same Area.
This seems to make nested routes like this no longer possible, as you would have to add a defined RoutePrefix such as "Home" to HomeController differentiate between the controller route matches. Perhaps this is solvable via a RouteConstraint or another mechanism, but I haven't been able to find a solution yet.
I'm thinking you need to change the [Route({action})] on each of your controllers to [Route({action=Index})].
Additionally, you mentioned having an Index action on your SectionController, but I don't see it in your code. I do see though that you have a Sections action which has [Route] listed above it. I'm guessing that the Sections action is actually what you want to get to when you go to /Admin/Sections, in which case you should remove the [Route] on the Sections action and change the [Route({action})] on the SectionController to [Route({action=Sections})].
Hope that helps. ;)

How to use receive POST data in asp.net web api?

I've googled a whole day but still can't find the answer. I need to POST data via jQuery.post to Web API MVC-4 but unable to. This is my routing:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
and this is my Controller (the GET works!):
public string Get(int id)
{
return "value";
}
public void Post([FromBody]string data)
{
//body...
}
This is the jQuery.post:
$.post('api/mycontroller', { key1: 'val1' });
Any idea ?
Edit:
#Darin: I tried this:
public class UnitDetails{
public string id { get; set; }
}
and:
public void Post(UnitDetails id) {
//body...
}
and:
$.post('api/mycontroller', {id:'string1'});
But still I miss something.. it doesn't stop in Post(...){...}. Again - Get(...){...} does work.. ?
This is by design and the only way to make this work with a primitive type such as a string is the following:
$.post('/api/mycontroller', '=' + encodeURIComponent('val1'));
So the body of the POST request must contain the following:
=val1
instead of:
data=val1
This has been discussed in this thread.
As an alternative you could define a view model:
public class MyViewModel
{
public string Data { get; set; }
}
and then have your controller action take this view model as parameter:
public void Post(MyViewModel model)
{
//body...
}
Contrary to primitive types, complex types use formatters instead of model binding. Here's an article which covers how does the Web API does parameter binding.
You're posting to api/mycontroller. ASP.NET MVC automatically appends the name supplied with 'Controller', so it's looking for a controller named mycontrollerController. The name of your API controller is not mentioned in your post, but I suspect it's not that.
Assuming that your controller is named 'myController', try posting to api/my.
$.post('api/my', { id: 'string1' });

Resources