Search code examples
owinmime-typesnancyplaintextcontent-negotiation

NancyFx conneg returning 406 from IResponseProcessor except for text/html content type


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


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