Question

I'm running Nancy on Microsoft.Owin.Host.IIS (Helios).

I'm trying to wire up conneg via IResponseProcessor to respond to an Accept header of text/plain, but it will only return 406.

I've tried multiple content types, and nothing works.... EXCEPT, strangely, text/html (after clearing the base ViewProcessor).

public class ViewApiProcessor : IResponseProcessor
{
    private readonly IViewFactory viewFactory;

    public ViewApiProcessor(IViewFactory _viewFactory)
    {
        this.viewFactory = _viewFactory;
    }

    private static readonly IEnumerable<Tuple<string, MediaRange>> extensionMappings =
    new[] { new Tuple<string, MediaRange>("txt", MediaRange.FromString("text/plain")) };

    public IEnumerable<Tuple<string, MediaRange>> ExtensionMappings
    {
        get { return extensionMappings; }
    }

    public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context)
    {

        bool matchingContentType =
            requestedMediaRange.Matches("text/plain");

        return matchingContentType
            ? new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.ExactMatch }
            : new ProcessorMatch();
    }

    public Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context)
    {
        context.ViewBag.RequestType = "api";

        var response = (Response)this.viewFactory.RenderView(context.NegotiationContext.ViewName, model, GetViewLocationContext(context));

        return response.WithContentType("text/plain");
    }

    private static ViewLocationContext GetViewLocationContext(NancyContext context)
    {
        return new ViewLocationContext
        {
            Context = context,
            ModuleName = context.NegotiationContext.ModuleName,
            ModulePath = context.NegotiationContext.ModulePath
        };
    }
}

Then, in the module:

Get["/"] = p => 
{
    return Negotiate.WithView("Index");
};

UPDATED: I have edited the code above to show the proper combination of IResponseProcessor and Negotiator

Github Source

Was it helpful?

Solution

The more interesting thing here is, what does your routes look like? If you want it to be able to negotiate the response then you need to return a `Negotiator``

Get["/"] = _ => {
   return Negotiator.WithModel(...).WithView("foo");
};

If you return a plain Response (or anything that can be implicitly cast to a Response, such as string, int, HttpStatusCode or Action) then you will circumvent the content negotiation entirely as described here https://github.com/NancyFx/Nancy/wiki/Content-Negotiation.

If you are returning a view, using View[..] then you are saying that the only permissible media range is text/html

OTHER TIPS

Conneg is a solution looking for a problem:

HTTP content negotiation has four axes: negotiation by format (Accept), negotiation by character encoding (Accept-Charset), negotiation by natural language (Accept-Language) and negotiation by compression (Accept-Encoding). These axes need to be discussed separately.

  • Negotiation by format (Accept)

You can’t rely on format negotiation as Web author, because there are always clients that accept something they don’t declare. Due to the previous point, if you are a browser vendor and another vendor has shipped a browser that doesn’t declare that it supports something it supports, it doesn't make sense for you to waste bytes declaring it, either, because Web authors can’t rely (solely) on the declaration anyway.

  • Negotiation by character encoding (Accept-Charset)

Of the major browsers, only Chrome sends Accept-Charset anymore

  • Negotiation by natural language (Accept-Language)

When sites do have multiple language versions, the versions often are not equal. Typically, one language is the primary language for the site and the other language versions are incomplete, out of date or otherwise of low quality. Even if everyone configured with their language preferences in their browser, the combination of languages that the person can read would partition the population of the world into rather small buckets in some cases making the language combination the way to identify a particular user or a smallish group of users. Since people so rarely configure their language preference, when someone does configure it, chances are that the configuration becomes uniquely or almost uniquely identifying. This can be seen as a privacy problem.

  • Negotiation by compression (Accept-Encoding)

These days, all major browsers support gzipped responses. In that sense, the feature is a success. However, it is terribly wasteful that each request ends up containing 23 bytes of boilerplate.

References

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top