Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Trying to implement a system whereby a developer would create a cshtml Razor view as per usual (e.g. a series of @Html.EditorFor(t=>t.MyProperty) calls interspersed with markup etc.) but based on certain headers coming through in the request to differ the response.

As an example:

Standard user navigates to the action by typing the route into a browser, gets the standard, expected HTML result.

Non-human system visits the same URL as the user, but includes an HTTP header to identify itself, gets a custom output. Custom output in this case is JSON representation of the fields defined with the EditorFor calls.

I've gotten reasonably far with this by just creating a new helper called CustomEditorFor that dumps out the JSON if hte header's present or defers to HtmlHelper.EditorFor if it's not, but that gives me some limitations - I need each of the fields to be comma separated and wrapped up as a JSON collection but the info available to the helper doesn't appear to give me any context of whether it's first/last etc. in the view.

I'm looking into where I need to override to do this relatively cleanly. So far it's looking like I'll need a custom implementation of IView (or more likely subclassing RazorView) and a custom implementation of a ViewEngine (we're already subclassing RazorViewEngine to handle paths etc. so that's not a problem) and it's looking like, to get the absolute ideal result, I may also need to create a subclass of either WebViewPage or WebPageBase to be able to affect what's shown or not shown to the response etc.

I do have a couple of limitations here:

  1. I can't use a separate view file for this
  2. I can't put the bits for the JSON in the view file because of (1). It needs to render to HTML properly by default and only to JSON where the correct headers are present

Has anyone done anything along these lines? Am I on the right path? Is there a simpler way to do this? Feels incredibly overengineered but I can't see any other way to do this cleanly at the moment.

share|improve this question
Does your url structure have to be exactly the same for normal and machine based requests? You could use Web API with MVC4 which would respond in Json. The limitation being you'd have to use a different url which included /api/ in it somewhere. – Stokedout 29 mins ago
Unfortunately it's got to be the same, but you're along the right lines. Ultimately it's a case of the output being preferred as JSON with an HTML fallback so the alternate URL won't work without redirects which I'd like to avoid. – Chris Disley 25 mins ago
To give a bit more info, it's essentially that the application's designed to be used with a single-page-app or IOS/Android native app which is where the JSON requirement comes in, but the same URLs would be in play for a normal user browsing to the application which would result in HTML. Aim is to get the developers to create their viewmodels and views once, targeting HTML and for the devices and single page apps to be able to get just the data they need as JSON without having to render/parse the HTML. – Chris Disley 22 mins ago

1 Answer

One way that comes to mind, and which would require least amount of work on your part, is the following: Firstly, you can use DisplayModeProvider and register a mode for your particular Agent. Below is how you can register iPhone (browser). Use a Custom extension

 protected void Application_Start()
    {
        DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
        {
            ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
                ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
        });

       ...
    }

Secondly, you can write a view for each of your views with your custom extension i.e. myView.MyCustomExtention.cshtml

A request from your intended agent would make MVC render views with your custom extension

Thirdly, you can render your view inside the views with custom extension using @Html.RenderPartial()

Since you already have CustomEditorFor that renders the appropriate JSoN for each control, your newly created view gives you ability to add starting and ending brackets.

As far as the comma's are concerned, you can add a comma after each element in CustomEditorFor and use a dummy last element in JSON to get rid of the last comma (or in case there is only one element in your view)

Disclaimer: this is based on my theoretical knowledge. I have not yet done this on my own.

share

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.