Capturing an incoming webforms request in an MVC route - asp.net

I'm converting a legacy webforms app to MVC, working through it a page at a time. To make the project easier to work with I've moved all the webforms pages, which were previously in the route of the project into a /webforms subdirectory. So I need to capture any incoming requests for /page.aspx?param=123 and redirect them to /webforms/page.aspx?param=123. I thought an easy way to do this would be to setup a route handler that passed any such requests to a controller that does that job. I set up a route like so:
routes.MapRoute("WebformsRedirect", "{*page}",
new { controller = "Webforms", action = "ForwardToPage" },
new { page = #"\S+.aspx\S*" }
);
This kind of works but it doesn't capture the query string, only the page part. I can get the query string for the Request object in the controller so it's not a huge deal but it would be nice to be able to do it through the route only. My routing unit tests (which I copied from Steve Sanderson's MVC book) actually pass correctly when I test them with querystrings so I'm confused why it isn't working. Is my regular expression wrong? They aren't my strong point.

QueryStrings are not part of the routing
if you requested for example "Home/Index?foo=bar" and you have a route that match "Foo/Bar" to Controller Foo , Action Bar without any more routing info (don't know anything about foo) you still can write
class HomeController: Controller {
ActionResult Index(string foo) {
}
}
now foo variable will equal bar , why ?
because its the model binder that gets the value of the parameters passed.
the model binder check 4 repositories by default QueryString , Routing Place Holders ,FormsCollections and Files
so what i am trying to say , the route and QueryStrings are two different things , it doesn't need to capture it

Related

Attribute routing default url

I have the following method in my controller, using C# MVC with Attribute roouting:
[Route("")]
[Route("/something-else")]
public IActionResult Index(){
}
Im using two different routes to access this functionallity, since i want customers with bookmarks to the previous implementation to work.
The problem is that i cant specify which of these Routes will be the default one when i issue the action like this:
<a asp-controller="FOO" asp-action="Index">
Everythiing works as expected, both URLs work, but i cant specify which of these routes will be used when navigated to by the action, via the action above.
I would like that the first route be used everytime i navigate to this action, except when someone explicitly writes the old url into the browser.
Are there any default attributes to the [Route("")] tag?
The RouteAttribute class has an Order property. From the docs:
Gets the route order. The order determines the order of route execution. Routes with a lower order value are tried first.
For example:
[Route("/something-else", Order = 1)]
[Route("", Order = 2)]
public IActionResult Index(){
}
As an aside, I would strongly discourage you from serving the same page with multiple URLs. Google's indexing will give you worse ranking because of it. Instead, consider returning a redirect to the new URL instead.

ASP.NET Web API - method that is called for all requests prior to the routed method?

I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.

What's the right way to call an action from a different controller in ASP.Net MVC 6

I'm getting a
No route matches the supplied values
while trying to return a RedirectToAction("Action", "Controller"). The method signature says "actionName" and "ControllerName". I'm assuming actionName is the method name in the Controller, Am I correct? For ControllerName I'm using the Controller File Name without the Controller Sufix. Ex.:
return RedirectToAction("Index", "WebApp")
where Index is a method of WebAppController and the command is being issued from a method of AnotherController
Both the caller controller and the called one are on the same Controllers directory on the same application.
I'm cofused because in this ASP.net MVC application there is also Route attributes and Action attributes where you can put names on methods, different than the real method name. In my case I have no Route["Name"] nor [httpXXX("route", Name="dasdasdas")] configured for the methods involved in my attempt.
I have been reading MS docs and some examples but It appears I'm doing the thing right but for strange reasons it's not working. I even tried using Redirect("Controller/Action") and with it the problem vanishes but the new problem is this way of redirect doesn't support passing data parameters to the target route.
At this point I'm not working with Action links in Views, different from Form related ones.
I would really appreciate if at least anyone can give me a hint about where can I find info.
The right way to call an action from a different controller is the one I was using:
return RedirectToAction("AnActionMethodName", "AControllerWithoutControllerSufix"[, object values]);
My problem, after several hour spent was that I added two useMvc calls in the Startup.Configure(...)method:
app.UseMvc();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=MyApp}/{action=Index}/{id?}");
});
This was due to copy + paste of code. It was obviously confusing the MVC component in charge of attending the redirection part, as the right way I supose is tho use only one. (I'm newbie on .Net Platform and its frameworks)
Anyway I leave this as a reminder about the risks and consequences of copying and pasting code. At some point something weird can happen to your app and the cause can be a simple copy + paste error.

How to approach making a controller with (unlimited) db look-up routes

I'm digging into ASP.NET MVC from classic asp, and have completed some tutorials. I understand the concept now, but I have a main question about the controller. How are you able to control the url structure if you are getting your url's (with params) from a sql database?
Example: /custom-url-1 or /custom-url-23423411
(Returns params accordingly to feed the code)
I'm guessing it would have to do with ActionResult Index() , but not sure where to go after that. Any idea's where to look or is this even possible? Does MVC even allow this?
One Way you can approach this is to have everything go to one action in one controller and resolve the content in the view.
This is useful only if you have one type of view.
Second way is to have route constraint or a custom route constraint for each type of content you have
say : Galleries, Blog, Pages
and in each constraint check if the given url is of this type ( by db call), if the constraint returns true, it will point the request to the given controller and action.
The third way is to have a custom route handler that does the checking and routing (mind you this is probably the hardest task but works best if you have complex system, if your is simple try using method 1 or 2
P.S. if you want your urls separataed by "-" instead of "/" you can do just this
routes.MapRoute(
"Default", // Route name
"{controller}-{action}-{id}",// URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

MVC4 - Relationship between a View and a Controller

I'm having difficulty grasping the concept of MVC within .NET. I'm creating a basic Blog application, mainly to gain insight and skills with MVC. I just don't quite get some of it. The part I am currently unclear about is the relationship between a Controller and View. It would clear the matter up if someone would answer me this.
I have a View called TestA which corresponds to my Controller ControllerTestA. Now I've added the following inside the ControllerTestA.
public ActionResult TestA (){ //do something }
Now I need to know if all my postbacks in whatever form from view TestA will have to go through my TestA Controller method. So essentially I could have different postback with different parameters for different reasons. Some in use with one postback and others in use for another. Is that how it is done?
Would love some assistance here.
You are missing a crucial part of the relationship here, which is routing. You are speaking in terms of WebForms using terms like Postback; don't do that because you'll end up confusing yourself.
The best way to think about MVC is in Requests and Responses.
Let's look at how a request (high level) happens in an MVC application.
Request
Request hits the server with a url ex. /hello/world
That url is used to match any entries in your route table
When a match is found, that route defines basic values like what controller and action should be called.
The controller is created, and that action is called with the route values and other request properties (querystring, session, etc...).
Response
We are now in the controller action, run the code you need to fulfill the request
Pass the data to the View
The view is determined by convention and your ViewEngine
The view is then rendered and written to the response.
The request/response is finished.
Note
This whole process is determined by the route, and the relationship between the controller and view are trivial. Where the form is posted to is determined by you in the view by using helper methods that determine what route to hit in the next request/response flow.
Some Helper Methods.
#Url.Action("index", "home");
#Html.ActionLink("index", "home")
#using (Html.BeginForm("create", "home")) { }
To sum it all up, the relationship between the controller action and view is really facilitate by your routes. Once you have a grasp of how to create them, then you will better understand how to manage the interaction of your application. Hope that helps. :)
There is no such thing as "Postback" in MVC. In contrast to WebForms, a view only renders HTML to be sent to the browser. As soon as any type of request is issued by the browser, it goes to the controller, not to the view.
As for the relationships:
If you define a TestAController (note: Not "ControllerTestA"), it serves the "/TestA/*" URL's. If you have a method TestA in there it will serve "/TestA/TestA".
If your method returns View(someModel) it will look for a view named TestA.cshtml/TestA.aspx, named like your method, within a folder Views\TestA (named like your controller, without the "Controller" suffix)
The view will render the HTML based on the someModel passed by the controller.
Within the view you may call other URL's or post data to some. This closes the circle.
As for the parameters or overloads, there are some restrictions:
You can define overloads for GET vs. POST vs. PUT vs. DELETE. You will need to annotate the methods with the according attributes though.
However you cannot define multiple overloads of the same method name for POSTs with different sets of parameters. You will need to make your POST method signature such that parameters can or cannot be sent to the server:
Example:
public ActionResult TestA(
string someOptionalParameter, int? someOtherOptionalParam)
{
if (string.IsNullOrEmpty(someOptionalParameter)) { ... }
if (someOtherOptionalParam == null) { ... }
}
The model-mapper will set your parameters to null if they are not posted to the server.
Like Khalid already mentioned - you should not mix up the concepts of MVC and WebForms. They are very different. MVC has no such thing as a "view state" which could be posted to the server. It has no WebForm-like lifecycle for the ASPX (or CSHTML) pages.
If you have a form in a view, then that form has a url to which it will post to. This URL is in the Html.BeginForm method in your view.
The form will then be posted to the appropriate controller methond in the approoriate controller
So if BeginForm starts like this:
using (Html.BeginForm("AddProduct", "Product"
Then the action method "AddProduct" in the controller Product (ProductController is the class name) will be called.

Resources