8

I would like to ask whether it's a good approach to redirect from within a model instead of a controller.

The reason I want to do that is because it is easier to unit test redirection from a model (I just pass a mock redirector object to the model in my tests) as opposed to controller which is more difficult to unit test. It also keeps controller thinner as all I do in the controller is create an instance of the model and pass it parameters from the request object. There is not a single if/else in the controller this way.

Is it a bad practise?

Richard Knop
  • 81,041
  • 149
  • 392
  • 552
  • Apart from the answers given below, as you speak about testing, why do you unit-test redirects? I think you're looking for integration tests here therefore it should not go into the model (nor into the unit-tests). If you can share more of your motivation, it might be possible to give better answers. – hakre May 21 '13 at 13:21
  • 2
    Redirection concerns HTTP Request/Response, so it definitelly should be on the controller. Models should know nothing about requests/responses. Models should be reusable, independent of the method of access. If you need to use this very model in a non-web based application, you will not be able to. – Henrique Barcelos May 21 '13 at 14:43
  • @HenriqueBarcelos: One could additionally argue that controllers as well should be independent of the method of access. OP is probably asking about that which is really much harder to achieve then just keeping the models out of the equation. – hakre May 21 '13 at 14:46
  • Nop, Controllers and Views are NOT reusable. In web applications, server-side controllers will handle requests, in desktop applications, the "requests" are events performed on a interface element, such as a button, so this requires another completely different controller. – Henrique Barcelos May 21 '13 at 14:49
  • @HenriqueBarcelos: Software Developers with well known Names tell differently. I can accept they are all totally wrong, however, I could also consider they have something to say. Apart from that I could consider if those are putting that on topic, OP might ask about it. At least it's thinkable. – hakre May 21 '13 at 15:38
  • 2
    Could you post some reference of these software developers here? Maybe I did not get the best fonts, but what I always saw was this: Models should be reusable, Controllers and Views not. – Henrique Barcelos May 21 '13 at 15:56
  • What about exceptions? For example, my model may contain several API calls to a service, which may result in a few different exceptions that could be raised, and I want to redirect different based on which exception is raised. Should I always return an array of success and exception, so my controller can then handle the exception? I can't wrap the model call in a try catch as it could be triggered at different parts of the call which would result in different redirects... – Relequestual Sep 03 '13 at 14:20

5 Answers5

4

Most often in webapplications - MVC or not - redirects are implemented on a high-level layer. In non OOP code this often is a high level global function that knows a lot about the global static state and what represents a request and a response therein.

In more OOP driven sites, you find this often as a method with the "response" object (compare Symfony2 and HTTP Fundamentals; note that Symfony2 is not MVC), however, it often then has also some similar methods (e.g. see Difference between $this->render and $this->redirect Symfony2).

As most often those "response" objects are central in the web-application this qualifies as well as a high-level layer in my eyes.

From a testing standpoint - especially with integration testing - you normally do not need to test for redirects specifically. You should test that generally your redirect-functionality works on the HTTP layer so that parts of your application that make use of it can rely on it. Common problems are to not really follow the suggestions given in the HTTP/1.1 specs like providing a response body with redirects. A well working webapplication should honor that. Same for using fully qualified URIs.

So how does all fit this into MVC here? In a HTTP context this could be simplified as the following:

  • The response is to tell the user to go somewhere else.
  • If the user would not be important, the application could forward directly - that is executing a different command, the client would not be used for that, the redirect not necessary (sub-command).
  • The response is to say: Done. And then: See next here this command (in form of an URI).

This pretty much sounds like that the actual redirect is just some output you send to the client on the protocol level in client communication. It belongs into the interface of that protocol you want to support. So not into the model but into the client-interface and the boundary of the client interface inside an MVC application is the controller AFAIK.

So the redirect probably is a view-value-object with a special meaning. To get that working in a HTTP MVC you need a full URL abstraction which most PHP frameworks and libraries make a big round around because it's not well known how that works. So in the end I'd say: Do as Symfony2 has done, place it in a high level layer component, drop MVC and live with the deficiencies.

Everything else is hard to achieve, if you try otherwise, there is high risk to not stop with abstracting anymore.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
3

Neither controller nor model should be redirecting anything anywhere. HTTP Location header is form of a response, which strictly in purview of views.

Model layer deals with business logic, it should be completely oblivious even to the existence of presentation layer.

Basically, it goes down to this: controllers handle input, views handle output. HTTP headers are part of output.

Note: when dealing with Rails clones, it is common to see redirects performed in "controller". It is because what they call "controller" is actually a merger of view and a controller responsibilities. This is a side-effect of opting to replace real views with simple templates as the 3rd side of triad.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • 15
    Why shouldn't the controller do a redirect? After all it's intended to control the "GUI flow" of an application. Doing a redirect seems to be an intrinsic responsibility of the controller. The view cannot know anything about "where to go next" is should merely *display* data from the model. –  May 21 '13 at 12:41
  • @a_horse_with_no_name, because controllers responsibility is to alter the state of model layer. Not to produce an output. – tereško May 21 '13 at 12:43
  • @tereško So if I understand correctly the controller could state a redirect needs to happen. But the actual redirecting should not happen there. – Bart May 21 '13 at 12:48
  • 1
    How could the controller alter the state of the model if it can't "control" the "GUI flow"? Which component would be responsible to display an error message (e.g. because some business rule validation failed) after a form was submitted? The view cannot do it as it doesn't process the submitted data, the model cannot do it as it only reacts on data sent from the controller. –  May 21 '13 at 12:55
  • @Bart, yes, if you have a fully functional views. Basically there are two ways one could deal with it: 1. directly - controller tell view that there will be a redirect required (controllers can alter views too, but it is rarely done), 2. indirectly - view recognizes that there has been a "write" to model state (`POST`, `PUT`, etc.), or some condition based change, like user expiration. – tereško May 21 '13 at 12:56
  • 1
    @a_horse_with_no_name , please look up the definition of MVC pattern. – tereško May 21 '13 at 12:56
  • With redirection there is usually associated some condition (if A, redirect, if B, forward, otherwise do something else) which is a business logic in my eyes. Should there be business logic in the view then? – Richard Knop May 21 '13 at 13:06
  • Views contains something that one could broadly describe as "UI logic". They acquire data from model layer and based on said data create a response. "If model has error, show error template, else - show result template" is not business logic. And there is no significant difference between choosing which templates to combine or which headers to send. – tereško May 21 '13 at 13:11
  • All who disagree with @tereško have a look at how spring-webmvc handles it. https://github.com/SpringSource/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java. – Bart May 21 '13 at 13:15
  • 4
    This answer is wrong. The redirect is just a form of telling that you need to render a different view, and it belongs in the controller. Period. The actual performing of the redirect is a different matter, but it occurs after you figured out that you need a redirect. In ASP.NET MVC, your controller will generate a RedirectResult for you, and it is completely testable in unit tests. I would be surprised if there is no analogous construct in the framework the OP is using, so I suggest to search for it. – gabnaim May 21 '13 at 14:15
  • 2
    @gabnaim, what is "GUI flow"? I fail to find it [here](http://martinfowler.com/eaaDev/uiArchs.html) or [here](http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html), or [here](http://books.google.lv/books/about/?id=FyWZt5DdvFkC), or even [here](http://c2.com/cgi/wiki?ModelViewController). It seems that you are just blindly following some framework, which claims to implement MVC by virtue of having "MVC" in it's name. Please. **look up what controller's responsibilities are!**. Or are you one of those people, who refer to ASP.NET MVC frameworks as "MVC" and to IE as "the internet" ? – tereško May 21 '13 at 14:22
  • teresko, I am using words others used. Sorry, I didn't know you needed it to be explained. It means that when you redirect, you are rendering a different view. Thus, you "flow" somewhere else. Your models and views should not be deciding that they aren't the right views to be rendered. The controllers should do that. The OP asked for advice to where this logic goes. If I used too complicated words for you, my apologies. – gabnaim May 21 '13 at 14:42
2

I would say yes, it is wrong. As far as I understood, while models manage data and views manage layouts (i.e. how data should be displayed), controllers are only and exclusively in charge of the HTTP requests/responses management (in the case of a web app), and redirections typically belong to that tier in my opinion.

Examples in common frameworks

Symfony:

return $this->redirect($this->generateUrl('homepage'));

Spring MVC:

return "redirect:/appointments";
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • Yeah, I'm using Zend Framework 2, it works the same way as in Symfony. But I am keen on TDD and unit testing controllers is really difficult as you have to mock loads of objects (let's say the controller sits behind ACL and is accessible only for logged in users, then you need to mock the entire ACL). I am thinking whether controller should not just consist of free lines of code (1. create instance of model, 2. pass request variables to method in a model, 3. return response object) I don't think there should be a single if/else in a controller which is what you usually need to redirect. – Richard Knop May 21 '13 at 13:02
  • @sp00m spring-webmvc controllers do **not** redirect. The string you return is processed by a `ViewResolver` and return a `RedirectView`. See https://github.com/SpringSource/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java – Bart May 21 '13 at 13:12
  • @RichardKnop I do not really agree. Let's say you need to save a new product. Your controller handles the post request and gives a model's method the request parameters. The model then tries to persist the product, and returns `true` if it could or `false` if an error occured (simplified example of course). In my opinion, the controller is then in charge of either redirecting to a success page if persistance succeed, or staying on the previous page to display potential error messages. – sp00m May 21 '13 at 13:14
  • 1
    @Bart I would say this is the *technical way* redirections have been implemented. But functionally - or better from the developer point of view - you'll never implement redirections in your views (e.g. your JSP files) but rather in your controllers. – sp00m May 21 '13 at 13:23
  • @sp00m, what you are advocating for is the uses of **quick hacks** (which all developers like so much, when they write them), instead of solid application design based on separation of concerns principle. – tereško May 21 '13 at 13:32
  • @spOOm To put it simple: The view **is** the redirect. – Bart May 21 '13 at 13:42
  • 1
    I diagree with the part "models manage data and views layouts". Models should know NOTHING about views. In the Active Model strategy, are the views that knows models. They can be related by the Observer Pattern in this case, but the model will just notify whatever view is attached to it, without not even knowing who they are. – Henrique Barcelos May 21 '13 at 14:53
  • @teresko, the **quick hack** is when you put a redirect into your view because you are too lazy to write mocks you need to test your controllers, not the other way around. And no, a view or a model should not even have a clue about that a redirect is possible. The model's only job is to pass data to the view. And the view's only job is to display itself. That's what separation of concern means. – gabnaim May 21 '13 at 15:03
  • @gabnaim, so, where would you put the logic for pagination? Will your model know how large is "page"? It is strictly a responsibility of a view to know, how many items are in a page. Model you have no knowledge about the interface. The views contains UI logic. Dealing with redirects are part of that logic. Redirect is NOT "show a different view", instead it is "make different request" .. or how else will you implement P/R/G ? Also, i get the impression that , what you are advocating for are the Rails-like dumb templates. It's not even MVC at that point. – tereško May 21 '13 at 15:12
  • @tereško, of course your model knows about paging. Paging is core information passed into your servers when they pull data. Your view can't decide it will display 50 lines when your model only has 30. And your service will have to know that you want 50 lines and that you are on page 5. Thus paging is passed around at every level. Your view displays the page, does not alter it. It might have a paging form that submits to the server that you are going to page 6. Teresko, have you actually implemented a client server application, or are you just reading Martin Fowler? – gabnaim May 21 '13 at 15:24
  • @teresko, a redirect is absolutely **NOT** UI logic. UI logic is that your input button goes here, and that your label goes there, and if you press this button, then it will invoke this controller action. In a web application, View = HTML page. I think we are having a disconnect on what "making a request" means. Yes, you might have a link on your UI that makes a different request - but your user is required to click it, it goes to a controller, and the controller decides to redirect. Your HTML page does not redirect itself. If it does, it is bad design. – gabnaim May 21 '13 at 15:33
  • .. and there goes the *appeal to accomplishment*. Of course I have a dayjob as a clerk in shopping mall. Ehh .. **View is not a HTML page**. Views are instances, that deal with UI logic. They manipulate and/or combine multiple templates (when necessary), based on data the they acquire from model layer. Views decide what sort of response user must receive based on current state of model layer. Also, regarding the pagination - you blew it. You just now explained how you model layer has intimate knowledge about the interface. Which it shouldn't have. – tereško May 21 '13 at 15:41
  • @HenriqueBarcelos I believe it's a wording discord: I meant "models manage data and views (manage) layouts". I've edited, hope it's clearer. – sp00m May 21 '13 at 16:02
  • 1
    Oops, my bad, but now is not ambiguous anymore =). – Henrique Barcelos May 21 '13 at 16:14
  • @teresko, I am sorry that I got off track. I would just like to comment on this. **Views decide what sort of response user must receive based on current state of model layer.** I think we are talking about different paradigms. For me: View logic = layout logic. Model = data the view needs to display. Controller = interfaces to business logic/data server; makes decisions about what view to display, and constructs model. A redirect in my mind is a decision about what view to display. Therefore it goes into the controller. That's all. Sorry if I implied anything else. – gabnaim May 21 '13 at 16:41
  • @gabnaim, take for example some standard list view. The same view can have at least 4 standard responses: a standard list of items, an error message, and emtpy-list layout and "show item, because single one in list" layout. The view requested from model layer: *"give me item data"*. And, based on what it received, view assembles the response. If requesting data the view receives "user unauthorized", it might also just respond with an HTTP location header, that direct user to login page. – tereško May 21 '13 at 16:55
  • @tereško: Now I see the misunderstanding. I was talking of MVC as a strictly UI paradigm. You are using it for the entire logic flow, including data access. Your "model" = my "business/data access layer", which is outside of the UI paradigm. In "UI only MVC paradigm" (for lack of a better distinction), you have four views, four view models, and one controller action deciding which to display. This controller action would call the business layer, what you call "model". In your paradigm, the view does all of these. In mine, those actions are separated from each other, and from DAL. – gabnaim May 21 '13 at 17:21
  • @gabnaim May [this answer](http://programmers.stackexchange.com/a/175952/72730) help. – sp00m May 21 '13 at 17:43
  • @sp00m, that does help. The word is "presentation layer". Thanks. – gabnaim May 21 '13 at 17:43
  • @tereško Funny, it seems that we already had such a conversation (divergence?) few months ago `:)` (see [the link](http://programmers.stackexchange.com/a/175952/72730) I gave above). – sp00m May 21 '13 at 17:47
  • @gabnaim, actually think you still misunderstand. To display those 4 states of list there would be a single view, with multiple templates: the 4 templates for the "inside", then a template general layout, another template for navigation, etc. View assembles them and create the output. There are no "four model". I see model as a layer. THe above mentioned example would require maybe one or two service (some library and maybe recognitions service .. depends on requirements). [Read this](http://stackoverflow.com/a/5864000/727208), if you want a more complete picture of how I see it =P – tereško May 21 '13 at 17:56
  • @tereško, ok, I see, you meant by four "states" composing the view from templates. Yes, that's what the UI logic does. But let me ask you this. Say that you decide that your single record result should not be a template but a redirect to your detail page. Are you saying that this decision is entirely up to the view, upon finding only one record in the model? Also, can you please also give an example of what the controller would do in this example? Just to make sure I am understanding. – gabnaim May 21 '13 at 18:30
  • @gabnaim, yes, if you want to redirect, when finding only one entry, that should be done by view for two reasons. 1: controllers should not get gathering data from model layer, therefore there is no way for controller know how many entries there will be. Only the view "reads" data from model layer. 2: the browser-level redirect is done by sending header, which I consider to be a form of response. – tereško May 21 '13 at 18:54
  • @gabnaim, as for what controller does in those cases - not much. Keep in mind that this extremely simplified example. In general there would be two types of inputs that controller has to handle: page and filters. I would send "page" to the view, which in turn will know what range of items that "page" describes (as I mentioned, "how many items are in page" to me seems like strictly UI logic), and request model layer `'give me items from N till M'`. The filers (if any applied) would go to the model layer and stored in session to be reused on every subsequent request. – tereško May 21 '13 at 19:00
  • @tereško, I understand your paradigm now and I will take my hat off to your knowledge off the MVC pattern. However I must say that I am much more focused on solutions because words can mean many things to people - thus I like examples better. In this case, I still disagree that redirects should ever happen in a view. Why? The OP's concern was unit testing. I would hate to have to maintain a system where views redirect on their own. If your controllers are too anemic to deal with that - change your controllers. Don't cripple your application just for the sake of a pattern. (to be cont.) – gabnaim May 21 '13 at 23:37
  • (cont to @teresko) As far as the MVC pattern, there is a reason why there are so many frameworks that eschew the original. It is useful for a presentation layer but lumps everything else into the "model". Yes, I use the Microsoft MVC and I love it. Your UI has no clue about your business domain - what you call "model" - because the controller sits between. Thus they are completely decoupled. And the controllers **control** the flow of logic, thus completely unit testable. – gabnaim May 21 '13 at 23:47
  • The unit-testing seems to me as the minor issue in this case. If implemented properly, then the headers as whole would be handles by some abstraction. Which means that it can be mocked and tested separately. Regardless of whether the redirect is performed from view or controller. – tereško May 22 '13 at 07:43
0

I think that you could have a model for your applications work flow or navigation (in your model layer) and then have your controller translate the different concepts in your work flow/navigation model into what views are to be displayed.

Your work flow class/module could know about the different activities/phases/steps that are available to the user, and it model your application kind of like a state machine. So your controller would make calls to this module to update the state and would recieve a response telling the controller which activity/phase/step it should go to.

That way your work flow model is easy to test but it still doesn't know about your view technology.

Simon
  • 6,293
  • 2
  • 28
  • 34
0

Many mentioned in comments these thoughts, so here is a summary:

The logic to figure out whether you need a redirect and what your redirect should be belongs into the controller. The model simply takes the data a view needs. This happens AFTER you decided which view to render. Think of the redirect as an instruction to perform a different controller action.

I use ASP.NET MVC and the controllers generate a RedirectResult for this purpose, which are completely unit testable. I don't know what is supported in your framework, but this is what MVC would do:

public class MyController : Controller {

    public ActionResult ShowInfo(string id) {
         if( id == null ) {
             return new RedirectResult("searchpage");
         } else {
             return new ViewResult("displayInfo");
         }
    }
}

In your unit tests, you instantiate MyController and check the type of the result and optionally, the url or view name.

Whether your redirect is actually performed is not a unit test issue - that's essentially making sure your framework is working right. What you need to test is that you are giving the correct instruction (i.e. the redirect) and the correct url.

gabnaim
  • 1,103
  • 9
  • 13
  • How do you know ASP.NET correctly implements MVC? I'm just asking. It could have incorrect implementation of the pattern. – Richard Knop Sep 04 '13 at 10:40
  • Richard: if you mean, 'how do you know if ASP.NET has a bug in it?': you will find out during testing, just not during unit tests. Your unit tests should test your own code, not the framework. If you mean, 'does ASP.NET MVC correctly implement the MVC pattern?': in my view, patterns are simply a learning tool to summarize common practices. Therefore, there is no such thing as "correct implementation of a pattern" - only a correct or incorrect solution to a problem. I hope this answers your question. – gabnaim Sep 04 '13 at 14:13
  • I am not talking about bugs. I am talking about incorrect implementation of MVC. I disagree patterns are just a learning tool, they are clearly defined and just because something has MVC in its name doesn't mean it really is MVC. – Richard Knop Sep 07 '13 at 20:35
  • Richard: MVC is probably the pattern with the most "correct" versions. They are all valid in the environment they were born of. The original "MVC" is of a mainframe environment and lumps pretty much all logic into the model. This does not work in a distributed environment. .NET MVC is presentation layer only. Best practice is putting your business logic as a separate layer. -- In practice, if we use a framework, we need to learn how the framework intends to use a pattern. Academically, we can debate how it relates to the book. I can tell you - not much. :) – gabnaim Sep 11 '13 at 15:31