This will surprise some of you that know me or the company I work
for, but not all of our staff are experts with ASP.NET MVC. In fact, I
am hoping that the couple who aren’t will read this post and learn a
little bit more about the topic.
Since the actions of controllers in MVC are dealt with constantly, I think it is a good place to start. This post is going to briefly describe the different types of results that are available to you in ASP.NET MVC 3. I will show some of the code that makes them work, which should make all of this seem a lot less complicated.
When creating new controllers in ASP.NET MVC 3, they will come with one or more actions by default. This depends on whether you selected a template which includes extras for you. The Empty controller template comes with an Index action with a return value of type ActionResult.
Inheriting from the ActionResult are the following classes:
It is in the ViewResultBase abstract base class that we get access to all of our familiar data objects like: TempData, ViewData, and ViewBag.
PartialViews are not common as action results. PartialViews are not the primary thing being displayed to the user, that is the View. The partial view is usually a widget or something else on the page. It’s usually not the primary content the user sees.
This is the common return syntax, and it means that you’re returning a ViewResult.
That is actually a call to the base Controller.View method, which is just going to call through with some defaults.
The beauty of ASP.NET MVC is actually in its simplicity though,
because all that really did was create our ViewResult for us. If we take
a look at the method that is being called you can see that we’re just
taking a little shortcut and keeping our action clean of this code we
would otherwise repeat every time we wanted a ViewResult.
Notice how simple that really is. All it did was put the model data
in if we specified it, give the ViewResult the Controller properties
that we set already, and assign the viewName and masterName.
Keep in mind, that we already saw that the abstract method in the ActionResult was the ExecuteResult method. The last two things to look at with the ViewResultBase are the ExecuteResult method and its abstract method FindView, which is being implemented by ViewResult and PartialViewResult.
This method is also not very complicated. It checks to make sure we
have context, and then if we don’t have the ViewName then we get that
information from the RouteData. Remember in MVC that the name of action
is included in the RouteData, so we can use that as the default view
name. This means that in the Index action, if we just call View(), it
will give us a ViewName of “Index”.
We then get the view we’re looking for by calling the FindView abstract method, which means we’re calling through to either ViewResult and PartialViewResult. Those I am not going to get into the guts of, but each one is going to try to find the correct view based on the name using its collection of ViewEngines.
Once we have the view, we are able to tell it to render itself using the context and the TextWriter we give to it.
That’s all there is to a ViewResult.
Its ExecuteResult override is extremely simple.
It just puts what you specified directly into the response.
I really don’t think I need that code snippet for this one.
If you want to just send the contents of the file back with the array of bytes for the file, then you want the FileContentResult. It uses the response’s OutputStream and writes those bytes directly into the stream sending it down to the user.
If you want to transmit the file using its name, you can use FilePathResult, which will call through a whole bunch of layers finally down to the HttpResponse. Once there it is going to create a new FileStream for your file and write the stream to the response allowing the file to be accessed from your action.
If you’ve already got a stream you can use the FileStreamResult, which will read all of the data from your stream and then write it into the OutputStream to be send back in the response.
These really aren’t all that complicated, but if you want to have control over the file downloads in your application, this is a great way to do it. These give you the power to put any code you want in your action before you give back the FileResult.
This one lets you return any StatusCode you want and you can include a StatusDescription for specifics.
See how simple that is? It’s basically just two lines of code with some null checking included.
For redirecting to a route, it is going to generate a URL to the route using the UrlHelper’s GenerateUrl method. For the RedirectResult it is instead going to use the UrlHelpers GenerateContentUrl method.
Either of these two are useful, and both will maintain your TempData if you need to pass something along with the redirect, all you have to do is put it in TempData.
Since the actions of controllers in MVC are dealt with constantly, I think it is a good place to start. This post is going to briefly describe the different types of results that are available to you in ASP.NET MVC 3. I will show some of the code that makes them work, which should make all of this seem a lot less complicated.
When creating new controllers in ASP.NET MVC 3, they will come with one or more actions by default. This depends on whether you selected a template which includes extras for you. The Empty controller template comes with an Index action with a return value of type ActionResult.
ActionResult
The action result is a very generic return value for an action. This is because it is the abstract base class for other types of actions. It is actually a very simple class having only one method that needs implementing.public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); }
- ContentResult
- EmptyResult
- FileResult
- HttpStatusCodeResult
- JavaScriptResult
- RedirectResult
- RedirectToRouteResult
- ViewResultBase
- FileContentResult
- FilePathResult
- FileStreamResult
- HttpNotFoundResult
- HttpUnauthorizedResult
- PartialViewResult
- ViewResult
ViewResultBase, ViewResult, and PartialViewResult
The ViewResult is the most common concrete type you will be returning as a controller action. It has an abstract base class called ViewResultBase, which it shares with PartialViewResult.It is in the ViewResultBase abstract base class that we get access to all of our familiar data objects like: TempData, ViewData, and ViewBag.
PartialViews are not common as action results. PartialViews are not the primary thing being displayed to the user, that is the View. The partial view is usually a widget or something else on the page. It’s usually not the primary content the user sees.
This is the common return syntax, and it means that you’re returning a ViewResult.
return View();
protected internal ViewResult View() { return View(null, null, null); }
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 }; }
Keep in mind, that we already saw that the abstract method in the ActionResult was the ExecuteResult method. The last two things to look at with the ViewResultBase are the ExecuteResult method and its abstract method FindView, which is being implemented by ViewResult and PartialViewResult.
public override void ExecuteResult( ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData .GetRequiredString("action"); } ViewEngineResult result = null; if (View == null) { result = FindView(context); View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext( context, View, ViewData, TempData, writer); View.Render(viewContext, writer); if (result != null) { result.ViewEngine.ReleaseView(context, View); } }
We then get the view we’re looking for by calling the FindView abstract method, which means we’re calling through to either ViewResult and PartialViewResult. Those I am not going to get into the guts of, but each one is going to try to find the correct view based on the name using its collection of ViewEngines.
Once we have the view, we are able to tell it to render itself using the context and the TextWriter we give to it.
That’s all there is to a ViewResult.
ContentResult
The content result lets you define whatever content you wish to return. You can specify the content type, the encoding, and the content. This gives you control to have the system give whatever response you want. This is a good result to use when you need a lot of control over what you’re returning and it’s not one of the standards.Its ExecuteResult override is extremely simple.
public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } HttpResponseBase response = context.HttpContext.Response; if (!String.IsNullOrEmpty(ContentType)) { response.ContentType = ContentType; } if (ContentEncoding != null) { response.ContentEncoding = ContentEncoding; } if (Content != null) { response.Write(Content); } }
EmptyResult
There is no simpler result than the EmptyResult. All it does is override the ExecuteResult method and since that method is void, the method is empty.I really don’t think I need that code snippet for this one.
FileResult, FileStreamResult, FilePathResult, and FileContentResult
If you want specific actions to send files as the response, then the FileResult is for you. Sadly, the FileResult is abstract, so you’ll need to use one of the inheriting classes instead. Each of these actually just overrides the WriteFile method for the abstract FileResult class.If you want to just send the contents of the file back with the array of bytes for the file, then you want the FileContentResult. It uses the response’s OutputStream and writes those bytes directly into the stream sending it down to the user.
If you want to transmit the file using its name, you can use FilePathResult, which will call through a whole bunch of layers finally down to the HttpResponse. Once there it is going to create a new FileStream for your file and write the stream to the response allowing the file to be accessed from your action.
If you’ve already got a stream you can use the FileStreamResult, which will read all of the data from your stream and then write it into the OutputStream to be send back in the response.
These really aren’t all that complicated, but if you want to have control over the file downloads in your application, this is a great way to do it. These give you the power to put any code you want in your action before you give back the FileResult.
HttpStatusCodeResult
The HttpStatusCodeResult is as simple as the ContentResult. In fact, the two are quite similar since they both just directly modify the response object.This one lets you return any StatusCode you want and you can include a StatusDescription for specifics.
public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } context.HttpContext.Response.StatusCode = StatusCode; if (StatusDescription != null) { context.HttpContext.Response .StatusDescription = StatusDescription; } }
HttpNotFoundResult and HttpUnauthorizedResult
These two results are actually just implementing the HttpStatusCodeResult, which means that they are very simple and just set the StatusCode to 404 for the HttpNotFoundResult and 401 for the HttpUnauthorizedResult.JavaScriptResult
About as simple as plenty of the others, this is just a quick way of getting JavaScript returned from a action. It’s similar to the ContentResult, but it has the ContentType hardcoded to “application/x-javascript” and just writes out the Script property.public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } HttpResponseBase response = context.HttpContext.Response; response.ContentType = "application/x-javascript"; if (Script != null) { response.Write(Script); } }
JsonResult
This one is a bit more complex, but still not very. It also has hardcoded its ContentType, but what makes it a bit more complex is that it uses a hardcoded JavaScriptSerializer to serialize the JSON data before writing it directly to the response.public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (JsonRequestBehavior == JsonRequestBehavior.DenyGet && String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException( MvcResources.JsonRequest_GetNotAllowed); } HttpResponseBase response = context.HttpContext.Response; if (!String.IsNullOrEmpty(ContentType)) { response.ContentType = ContentType; } else { response.ContentType = "application/json"; } if (ContentEncoding != null) { response.ContentEncoding = ContentEncoding; } if (Data != null) { JavaScriptSerializer serializer = new JavaScriptSerializer(); response.Write(serializer.Serialize(Data)); } }
RedirectResult and RedirectToRouteResult
These to are a little bit more complex, but both are ways of redirecting. Each one can either be a permanent or temporary redirect and they both just use the Redirect methods on the Response object.For redirecting to a route, it is going to generate a URL to the route using the UrlHelper’s GenerateUrl method. For the RedirectResult it is instead going to use the UrlHelpers GenerateContentUrl method.
Either of these two are useful, and both will maintain your TempData if you need to pass something along with the redirect, all you have to do is put it in TempData.
No comments:
Post a Comment