I need to use System.Web.Routing.RequestContext in a view model in order to call HtmlHelper.GenerateLink().
In MVC 1.0 it was possible to get the context statically by casting the current IHttpHandler:
var context = ((MvcHandler) HttpContext.Current.CurrentHandler).RequestContext;
Now the project has been upgraded to MVC 2.0 and this exception is thrown on the cast:
Unable to cast object of type 'ServerExecuteHttpHandlerWrapper' to type 'System.Web.Mvc.MvcHandler'.
I'm not sure if it's relevant but this is being run in .NET 4.0 on IIS6.
I need to use System.Web.Routing.RequestContext in a view model in order to call HtmlHelper.GenerateLink().
While in theory you could write:
var rc = HttpContext.Current.Request.RequestContext;
in practice you should absolutely never be doing something like this in a view model. That's what HTML helpers are supposed to do:
public static MvcHtmlString GenerateMyLink<MyViewModel>(this HtmlHelper<MyViewModel> html)
{
MyViewModel model = html.ViewData.Model;
RequestContext rc = html.ViewContext.RequestContext;
//TODO: use your view model and the HttpContext to generate whatever link is needed
...
}
and in your strongly typed to MyViewModel view simply:
<%= Html.GenerateMyLink() %>
I don't know what you wanna do with the System.Web.Routing.RequestContext? check out:
var context = new HttpContextWrapper(System.Web.HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(context);
// Use RouteData directly:
var controller = routeData.Values["controller"];
// Or with via your RequestContext:
var requestContext = new RequestContext(context, routeData);
controller = requestContext.RouteData.Values["controller"]
Related
I have a situation where I need to redirect to an ASP.NET MVC action in a different controller. I can't use RedirectToAction because I must POST the action's parameters to keep them out of the URL.
I attempted to instantiate and call the other controller's action directly like this:
OtherController myOtherController = new OtherController();
myOtherController.ControllerContext = new ControllerContext(this.ControllerContext.RequestContext, myOtherController);
return await myOtherController.Edit(myGuid);
When I do this, the other controller's code executes, but I end up with this error:
The model item passed into the dictionary is of type
'System.Data.Entity.DynamicProxies.OtherModel_BBCEF7C9378F4C4F097CC08FA2E508B8BD8D865E33093E31959919087A31348E',
but this dictionary requires a model item of type 'ThisModel'.
Does anyone know if I can get this working using the current approach? Assuming I must use an HTTP POST action and cannot have parameters in the URL, is there a different approach that you would recommend to achieve this result (other than combining the controllers)?
Edit:
Note that I don't think I can post directly from the client because I would need to nest Html.BeginForm.
You should really just be using RedirectToAction to push the browser to the action you want instead of trying to do it like this. As you can see from #Andrei Olariu code a lot of things happen under the hood during the construction of your controller (DI then context and parameter mappings) that really shouldn't be done manually and can easily be screwed up leading to hours wasted wondering why certain things are not behaving as expected.
I hope I understood what you're trying to do. This is what I'm successfully using to execute an action on a different controller.
private void ExecuteErrorAction(string action, HttpContextWrapper httpContext, Exception exception)
{
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = action;
routeData.Values["exception"] = exception;
IController errorController = DependencyResolver.Current.GetService<ErrorController>();
var context = new RequestContext(httpContext, routeData);
errorController.Execute(context);
}
I think that in your case this could look like:
private void ExecuteOtherAction(string myGuid, HttpContextWrapper httpContext)
{
var routeData = new RouteData();
routeData.Values["controller"] = "OtherController";
routeData.Values["action"] = "Edit";
routeData.Values["myGuid"] = myGuid;
IController otherCntroller = DependencyResolver.Current.GetService<OtherController>();
var context = new RequestContext(httpContext, routeData);
otherCntroller.Execute(context);
}
This is assuming that your Edit action on the OtherController takes a string called myGuid as a parameter.
Let me know if this helps.
Iam a beginner in MVC.
If iam using below code then Model.IsValid is not validating the object which in this case is Customer.
public ActionResult Submit()
{
Customer custObj = new Customer();
custObj.CustomerCode = Request.Form["CustomerCode"];
custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
While if Iam passing the Customer object in parameter then Model.IsValid is working perfectly.
public ActionResult Submit(Customer obj)
{
//Customer custObj = new Customer();
//custObj.CustomerCode = Request.Form["CustomerCode"];
//custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
Can any1 help me in getting to know the reason.
It doesn't work beause MVC never bound to the model itself. You manually overrode it so MVC has no clue whether the model is valid or not. It doesn't event know that custObj is the model.
ModelState.IsValid is set before your action method is called, so in your second example, when you allow MVC to bind to the model itself, it works. In the first, it doesn't work because you create the model and do manual binding to it.
Update
You can, however, also manually run the model validation by calling ValidateModel or TryValidateModel on the controller.
Documentation:
ValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.validatemodel(v=vs.100).aspx
TryValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryvalidatemodel(v=vs.100).aspx
As mentioned in other answers, your model is already validated before the action 'Submit' is called. So, when you are changing the model from inside your action, you will have to manually validated the model. You may use below code for it.
var context = new ValidationContext(custObj, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(custObj, context, validationResults, true);
if (isValid)
return View("Load", obj);
else
return View("EnterCustomer");
use below url for further details.
http://odetocode.com/blogs/scott/archive/2011/06/29/manual-validation-with-data-annotations.aspx
I have an ASP.NET app. My app has a _ViewStart.cshtml file. That file looks like this:
#using MyCompany.MyApp;
#{
Layout = "/Views/Shared/_Layout.cshtml";
var p = HttpContext.Current.Request.QueryString["parameter"];
ViewBag.QSParameter = p;
}
When I execute this code, I get the following error:
The name 'HttpContext' does not exist in the current context
I don't understand. Isn't _ViewStart.cshtml kind of the "shell" for the views? I'm trying to figure out how to globally read a query string parameter and set a value on the ViewBag for each request. I thought this would be the way to do it.
Thanks
You should have access to Request in your _ViewStart file.
Try this:
#using MyCompany.MyApp;
#{
Layout = "/Views/Shared/_Layout.cshtml";
var p = Request.QueryString["parameter"];
ViewBag.QSParameter = p;
}
EDIT: For ASP.NET 5
I don't have ASP.NET 5 on my machine but have looked at the source code for the framework. It looks like there is a Context property on RazorPage that returns an HttpContext. Alternatively, you can access the HttpContext through the ViewContext. See below:
#{
Layout = "/Views/Shared/_Layout.cshtml";
var p = Context.Request.Query["parameter"];
// or this...
// var p = ViewContext.HttpContext.Request.Query["parameter"];
ViewBag.QSParameter = p;
}
To retrieve it from _ViewStart.cshtml, you can use:
ViewBag.QSParameter = Context.Request.Query["parameter"];
Note: Use Query now (over QueryString) in ASP.NET 5
However, I might ellect to go a different route and take advantage of IResultFilter:
public class QSParameterFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
var QSParameter = context.HttpContext.Request.Query["parameter"];
((Controller)context.Controller).ViewBag.QSParameter = QSParameter;
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
Then, register it within your Startup.cs:
services.AddMvc();
services.Configure<MvcOptions>(options => {
options.Filters.Add(new QSParameterFilter());
});
Make the "Build Action = Content" in the file properties. This will solve the issue.
In the process of writing an unit test for a Controller, I need to setup or initialize -
ControllerContext.HttpContext.Request.QueryString
What is the simplest way for setting this up so that I can actually pass the - ControllerContext.HttpContext.Request.QueryString - and have the controller tested?
You could use a mock framework in order to mock the HttpContext that the controller is working with.
For example with NSubstitute:
// arrange
var contextMock = Substitute.For<HttpContextBase>();
var requestMock = Substitute.For<HttpRequestBase>();
var queryString = new NameValueCollection();
queryString["foo"] = "bar";
requestMock.QueryString.Returns(queryString);
contextMock.Request.Returns(requestMock);
var sut = new SomeController();
sut.ControllerContext = new ControllerContext(contextMock, new RouteData(), sut);
// act
var actual = sut.SomeAction();
// assert
...
Of course you could have used any other mocking framework you like such as Moq or Rhino Mocks in order to mock the HttpContext.
By the way if you used view models (which you should always do in a properly designed ASP.NET MVC application) you wouldn't even cared about Request.QueryString because your controller action would now have taken a view model as argument:
public ActionResult SomeAction(MyViewModel model)
{
...
}
and the default model binder would have taken into account the binding of request parameters to model. You should avoid writing plumbing code in your controller action where you are extracting some data from query strings and stuffs.
Look how much more readable your unit test now becomes:
var model = new MyViewModel();
model.Foo = "bar";
var sut = new SomeController();
// act
var actual = sut.SomeAction(model);
// assert
...
See? You don't need to care about plumbing code anymore.
if i have an route like /foo/bar/pewpew .. is it possible to get an instance of the controller which that route maps too?
To get the controller name, you can call create a fake HttpContextBase that returns your URL in its Request, then pass it to RouteTable.Routes.GetRouteData and check the area and controller values.
To get the controller instance, pass a RequestContext consisting of that HttpContextBase and RouteData to ControllerBuilder.Current.GetControllerFactory.CreateController.
Try,
var wrapper=new HttpContextWrapper(System.Web.HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(wrapper);
var controller = ControllerBuilder.Current.GetControllerFactory().CreateController(new RequestContext(wrapper, routeData), routeData.Values["controller"].ToString());
Update, you can use this instead.
var wrapper = new HttpContextWrapper(new System.Web.HttpContext(new HttpRequest(null, "http://localhost:4836/", null), new HttpResponse(new StringWriter())));