how to get partialview string in a apicontroller in mvc - asp.net-mvc

i have a apicontroller like this:
public class MenuController : ApiController
{
[HttpGet]
public string GetMenu([FromUri]string page, [FromUri]string menu)
{
}
}
I have a partialview say "menu.cshtml" i want to use that partialview and give the menu in string .
I have tried various functions that says renderpartialviewtostring but they use controller in it but i am using ApiController
Please help

You can derive your own type from IHttpActionResult and do it.
This article speaks about it - http://www.strathweb.com/2013/06/ihttpactionresult-new-way-of-creating-responses-in-asp-net-web-api-2/
You will need a reference to RazorEngine - http://www.nuget.org/packages/RazorEngine/
In your case you can create a StringActionResult derived from IHttpActionResult that does something similar to below.
public class StringActionResult : IHttpActionResult
{
private const string ViewDirectory = #"c:\path-to-views";
private readonly string _view;
private readonly dynamic _model;
public StringActionResult(string viewName, dynamic model)
{
_view = LoadView(viewName);
_model = model;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
var parsedView = RazorEngine.Razor.Parse(_view, _model);
response.Content = new StringContent(parsedView);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
return Task.FromResult(response);
}
private static string LoadView(string name)
{
var view = File.ReadAllText(Path.Combine(ViewDirectory, name + ".cshtml"));
return view;
}
}
and then in your controller, do something like this.
public class MenuController : ApiController
{
[HttpGet]
public StringActionResult GetMenu([FromUri]string page, [FromUri]string menu)
{
return new StringActionResult("menu", new {Page: page, Menu: menu});
}
}

Related

Unit testing custom attribute value in OnActionExecuting event

I have following implementation in my base controller from which I am deriving most of my controllers. It accounts for setting the page title, meta description and keywords for each page if the values are not set via decorator on the controller actions.
BaseController
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Page title
var title = filterContext.ActionDescriptor.GetCustomAttributes(typeof(PageTitleAttribute), false);
if (title.Length == 1)
ViewBag.Title = ((PageTitleAttribute)(title[0])).Parameter;
else
ViewBag.Title = "My website title";
//Page keywords
var keywords = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MetaKeywordsAttribute), false);
if (keywords.Length == 1)
ViewBag.MetaKeywords = ((MetaKeywordsAttribute)(keywords[0])).Parameter;
else
ViewBag.MetaKeywords = "targeted SEO keywords";
//Page description
var description = filterContext.ActionDescriptor.GetCustomAttributes(typeof(MetaDescriptionAttribute), false);
if (description.Length == 1)
ViewBag.MetaDescription = ((MetaDescriptionAttribute)(description[0])).Parameter;
else
ViewBag.MetaDescription = "My custom description";
base.OnActionExecuting(filterContext);
}
}
The custom attributes are fairly simple:
public class PageTitleAttribute : Attribute
{
private readonly string _parameter;
public PageTitleAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
public class MetaDescriptionAttribute : Attribute
{
private readonly string _parameter;
public MetaDescriptionAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
public class MetaKeywordsAttribute : Attribute
{
private readonly string _parameter;
public MetaKeywordsAttribute(string parameter)
{
_parameter = parameter;
}
public string Parameter { get { return _parameter; } }
}
And this is how make use of the attributes on appropirate action methods in controller:
public class HomeController : BaseController
{
[PageTitle("My new website")]
[MetaKeywords("Explicitly set keywords")]
[MetaDescription("description goes here")]
public ActionResult Index()
{
return View();
}
public ActionResult Error()
{
return View();
}
}
This all seems to work just fine. Now I would like to create unit test to validate that if the values are not set via attribute on action methods, the default values will be rendered as set from base controller. How can I do that? I have some tests written but I don't think they are targeting the filterContext on the basecontroller. Specifically I am looking for test for Error action method which does not have anything attribute value set. Just for reference this is what I have setup now:
[TestMethod]
public void Attribute_when_set_should_return_attribute_values()
{
var method = typeof(HomeController).GetMethod("Index");
var pageTitle = method.GetCustomAttributes(typeof(PageTitleAttribute), false)
.Cast<PageTitleAttribute>()
.SingleOrDefault();
var keywords = method.GetCustomAttributes(typeof(MetaKeywordsAttribute), false)
.Cast<MetaKeywordsAttribute>()
.SingleOrDefault();
var description = method.GetCustomAttributes(typeof(MetaDescriptionAttribute), false)
.Cast<MetaDescriptionAttribute>()
.SingleOrDefault();
Assert.IsNotNull(pageTitle);
Assert.IsNotNull(keywords);
Assert.IsNotNull(description);
Assert.AreEqual("My new website", pageTitle.Parameter);
Assert.AreEqual("Explicitly set keywords", keywords.Parameter);
Assert.AreEqual("description goes here", description.Parameter);
}

Unity how to pass Request in Controller's constructor from Unity

The old controller code with Concrete dependencies:
public SomeController: Controller
{
public SomeController()
{
}
public ActionResult Default()
{
**Something something = new Something(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString());**
something.SomeMethod();
}
}
The new Controller code with TDD focus:
public SomeControllerNew: Controller
{
private readonly ISomething _something;
public SomeControllerNew(ISomething something)
{
_something = something;
}
public ActionResult Default()
{
_something.SomeMethod();
}
}
PROBLEM:
Now in new TDD approach i need to invoke constructor where I am registering the Interface. I have put it in UnityBootstraper common file, Something like:
var container = new UnityContainer();
container.RegisterType();
**Something something = new Something(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString());**
something.SomeMethod();
This is not working here. Error is quite clear:
Object reference required for non-static field, method, property 'System.Web.Mvc.Controller.Request.get'.
I can't figure out how i can access http request here in UnityBootstrapper?
Edit:
Trying to do all this in RegisterRoutes.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
DependencyResolver.SetResolver(new Unity.Mvc3.UnityDependencyResolver(UnityBootstrapper.Initialise()));
var container = new UnityContainer();
container.RegisterType<ISometing, Something>();
}
}
One way to do it is to create an abstract factory like this:
public interface ISomethingFactory
{
ISomething Create(string url);
}
public class SomethingFactory : ISomethingFactory
{
public ISomething Create(string url)
{
return new Something(url);
}
}
And make your controller depend on it like this:
public class SomeControllerNew: Controller
{
private readonly ISomething _something;
public SomeControllerNew(ISomethingFactory somethingFactory)
{
_something = somethingFactory.Create(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString();
}
public ActionResult Default()
{
_something.SomeMethod();
}
}
A better approach (IMO) is to use a custom Controller Factory instead of using the Dependency Resolver like this:
public class CustomFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var request = requestContext.HttpContext.Request; //Here we have access to the request
if (controllerName == "Some") //Name of controller
{
//Use the container to resolve and return the controller.
//When you resolve, you can use ParameterOverride to specify the value of the string dependency that you need to inject into Something
}
return base.CreateController(requestContext, controllerName);
}
}
This way you don't have to introduce the ISomethingFactory, and your controller would still depend on ISomething directly.
You would need to tell the MVC framework about this custom controller factory like this (in Application_Start):
ControllerBuilder.Current.SetControllerFactory(new CustomFactory());

Can I move common code from a method into a base controller with MVC4?

I have the following method in five controllers:
public ActionResult Index(string page, string title) {
var vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
// difference code here for each controller
}
All my controllers inherit from a controller called BaseController.
Is there a way I could move this code into my base controller and call it? If so then what would be the best way to implement this?
This is an exact candidate for the Repository Pattern.
You could create all of these in your Repository class and call that method in each ActionResult method
public void Repository : IRepository
{
public GetMyBaseViewModel()
{
//..implementation here
}
}
public interface IRepository
{
BaseViewModel GetMyBaseViewModel();
}
....
and in your controllers :
...
public class HomeController : Controller
{
//private repository member
private readonly IRepository _repository;
//controller constructors
//injecting the repository here
public HomeController() : this(new Repository())
{
}
public HomeController(IRepository repository)
{
_repository = repository;
}
//methods that call the repository for the vm data context
public ActionResult Index()
{
var vm = _repository.GetMyBaseViewModel();
return View();
}
}
You could make an abstract ActionResult method in your base controller:
protected BaseViewModel vm;
public ActionResult Index(string page, string title) {
vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
try
{
return IndexSupplemental();
}
catch(NotImplementedException ex)
{
// Log and move on; the abstract method is not implemented.
}
return View();
}
protected abstract ActionResult IndexSupplemental();
Then every controller would have to implement this abstract method.
You can move it to a method in your base controller and call it when you need it.
public class BaseController : Controller
{
protected BaseViewModel _viewModel;
public void InitializeViewModel() {
vm = new BaseViewModel();
vm.Role = GetRoleNumber(User);
vm.MenuItems = contentService.GetMenuItems("00", vm.Role);
vm.Menu = pageService.GetMenu(vm.MenuItems, Request.FilePath);
}
}
An example:
public class MyController : BaseController
{
public ActionResult Index(string page, string title)
{
InitializeViewModel();
DoSomething(_viewModel);
}
}
In my projects most of my actions will return a viewmodel that inherits from the BaseViewModel but there are exceptions to this. So what I did was something like this in ControllerBase:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var authData = GetUserData();
if (authData != null)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
var vm = result.Model as ViewModelBase;
if (vm != null)
{
vm.UserId = authData.UserID;
vm.UserName = User.Identity.Name;
}
}
}
}
What you could do otherwise, as I expect your ViewModel to be of different types, is to create a method similar to this in ControllerBase:
NOTE This does not do what you want. I'm just showing a technique for creating a new instance of a derived class with some initialization code.
protected T Command<T>() where T : BaseCommand, new()
{
var command = new T();
command.IP = Request.UserHostAddress;
if (User != null && User.Identity.IsAuthenticated)
{
var authData = GetUserData();
if (authData != null)
{
command.UserId = authData.UserID;
}
}
return command;
}
Which would be used as
var command = Command<CreateUserCommand>();

How to get view and render it using custom ViewEnging in MVC?

I need to get view/partialview from some location(not default location) and to render it.
I thought to create custom ViewEngine. I thought the following:
1 - Return Plugin as action result that takes pluginName in constructor
public class PluginController : Controller
{
[HttpPost]
public ActionResult LoadPlugin(string pluginName)
{
return new Plugin(pluginName);
}
}
public class Plugin : ActionResult
{
private readonly string PluginName;
public PEditorPlugin(string pluginName)
{
this.PluginName = pluginName;
}
public override void ExecuteResult(ControllerContext context)
{
var engine = new MyViewEngine();
string viewContent = // Here I need some how to take the view with partialName and to render it
context.RequestContext.HttpContext.Response.Write(content);
}
}
2 - In ExecuteResult I'll create instance of MyViewEngine and somehow take the view and render it. But how to do it I don't know!
public class MyViewEngine : WebFormViewEngine
{
public override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new WebFormView(partialPath, null);
}
public MyViewEngine ()
{
// create our partial views and common shared locations
PartialViewLocationFormats = new[] {
"~/PluginsArchive/{0}.ascx"
};
}
}
Sow, how I can take the view and render it?
P.S. If you have any other suggestion I'll be glad.

How can I inherit an ASP.NET MVC controller and change only the view?

I have a controller that's inheriting from a base controller, and I'm wondering how I can utilize all of the logic from the base controller, but return a different view than the base controller uses.
The base controller populates a model object and passes that model object to its view, but I'm not sure how I can access that model object in the child controller so that I can pass it to the child controller's view.
A couple points. You can type your return value as ViewResult if you know that's all you're going to return. Then you can interrogate that value from the overridden implementation. More importantly, according to the MVC v1 source, calling View(object) simply sets the ViewData.Model on the controller, then constructs a ViewResult.
Controller.cs:440
protected internal ViewResult View(object model) {
return View(null /* viewName */, null /* masterName */, model);
}
Controller.cs:456
protected internal virtual ViewResult View(string viewName, string masterName, object model) {
if (model != null) {
ViewData.Model = model;
}
return new ViewResult {
ViewName = viewName,
MasterName = masterName,
ViewData = ViewData,
TempData = TempData
};
}
So all you need to do is call the base method and call View(string).
namespace BaseControllers
{
public class CoolController
{
public virtual ViewResult Get()
{
var awesomeModel = new object();
return View(awesomeModel);
}
}
}
public class CoolController : BaseControllers.CoolController
{
public override ViewResult Get()
{
var ignoredResult = base.Get();
// ViewData.Model now refers to awesomeModel
return View("NotGet");
}
}
Of course you waste CPU cycles constructing the ViewResult that you ignore. So instead you can do this:
public class CoolController : BaseControllers.CoolController
{
public override ViewResult Get()
{
var baseResult = base.Get();
baseResult.ViewName = "NotGet";
return baseResult;
}
}
If your base controller returns ActionResult, you'll have to cast it to ViewResult before changing the ViewName.
Sample from my app:
Base class:
public abstract class BaseTableController<T,TU> : BaseController where TU : IGenericService<T>,IModelWrapperService
{
protected readonly TU _service;
public BaseTableController(TU service)
{
_service = service;
_service.ModelWrapper = new ControllerModelStateWrapper(ModelState);
}
public ActionResult Index()
{
return View(_service.List());
}
Inherited:
public class SeverityController : BaseTableController<Severity, ISeverityService>
{
public SeverityController(ISeverityService service)
: base(service)
{
}
//NO CODE INSIDE
}
SeverityController.Index() leads to Views/Severity/Index.aspx. Just had to prepare view. Severity is one of dictionared in my bug tracking application. Every dictionary has similar logic, so I could share some code.
Based on the feedback given on this thread, I've implemented a solution like the one proposed by Antony Koch.
Instead of using an abstract method, I used a concrete, virtual GetIndex method so that I could put logic in it for the base controller.
public class SalesController : Controller
{
// Index view method and model
public virtual ActionResult GetIndex()
{
return View("Index", IndexModel);
}
protected TestModel IndexModel { get; set; }
public virtual ActionResult Index()
{
ViewData["test"] = "Set in base.";
IndexModel = new TestModel();
IndexModel.Text = "123";
return GetIndex();
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Index(TestModel data, FormCollection form)
{
TryUpdateModel(data, form.ToValueProvider());
IndexModel = data;
return GetIndex();
}
}
// This class will need to be in a different namespace or named differently than the
// parent controller
public class SalesController : MyApp.Controllers.BaseControllers.SalesController
{
// Index view method and model
public override ActionResult GetIndex()
{
return View("ClientIndex", IndexModel);
}
public override ActionResult Index()
{
return base.Index();
}
[AcceptVerbs(HttpVerbs.Post)]
public override ActionResult Index(TestModel data, FormCollection form)
{
return base.Index(data, form);
}
}
public class BaseController : Controller {
protected BaseController() {}
public ActionResult Index()
{
return GetIndex();
}
public abstract ActionResult GetIndex(); }
public class MyController : BaseController {
public MyController() {}
public override GetIndex()
{
return RedirectToAction("Cakes","Pies");
}
}
Just use abstraction to call the bits you need from the sub-classes.
I ended up just putting an extra parameter on the base Controller -- viewName.
Seems to work just fine.
Am I missing any major downsides?
public class SalesController : Controller
{
public virtual ActionResult Index(string viewName)
{
ViewData["test"] = "Set in base.";
TestModel model = new TestModel();
model.Text = "123";
return String.IsNullOrEmpty(viewName) ? View(model) : View(viewName, model);
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Index(TestModel data, FormCollection form, string viewName)
{
TryUpdateModel(data, form.ToValueProvider());
return String.IsNullOrEmpty(viewName) ? View(data) : View(viewName, data);
}
}
public class SalesController : MyApp.Controllers.BaseControllers.SalesController
{
public override ActionResult Index(string viewName)
{
return base.Index("ClientIndex");
}
[AcceptVerbs(HttpVerbs.Post)]
public override ActionResult Index(TestModel data, FormCollection form, string viewName)
{
return base.Index(data, form, "ClientIndex");
}
}

Resources