c# - 如何得到 Nancy 谈判代表的回应?

标签 c# nancy content-negotiation

我有一个 NancyContext,我需要得到一个 Response,其正文基于请求的正确内容协商器。我想我可以使用 Nancy 的 Negotiator 类来添加模型、设置状态和其他内容。但是,我需要返回 Response 的子类型。那么,我可以使用什么来使用 Negotiator 构建响应?

这是我的方法:

public Response ConvertToHttpResponse(Exception exception, NancyContext context)
{
    var negotiator = new Negotiator(context)
        .WithStatusCode(HttpStatusCode.BadRequest)
        .WithReasonPhrase(exception.Message);

    return ???;
}

最佳答案

我个人更喜欢使用 Nancy 谈判器仅返回“Happy Path”结果(即 View /jsondto 返回),然后为可能发生的任何错误返回 vanilla nancy Response 对象。

这样做的一种方法是直接在您的模块中返回错误,例如:

public class ProductsModule : NancyModule
{
    public ProductsModule()
        : base("/products")
    {
        Get["/product/{productid}"] = _ => 
        {
            var request = this.Bind<ProductRequest>();

            var product = ProductRepository.GetById(request.ProductId);

            if (product == null)
            {
                var error = new Response();
                error.StatusCode = HttpStatusCode.BadRequest;
                error.ReasonPhrase = "Invalid product identifier.";
                return error;
            }

            var user = UserRepository.GetCurrentUser();

            if (false == user.CanView(product))
            {
                var error = new Response();
                error.StatusCode = HttpStatusCode.Unauthorized;
                error.ReasonPhrase = "User has insufficient privileges.";
                return error;
            }

            var productDto = CreateProductDto(product);

            var htmlDto = new {
              Product = productDto,
              RelatedProducts = GetRelatedProductsDto(product)
            };

            return Negotiate
                    .WithAllowedMediaRange(MediaRange.FromString("text/html"))
                    .WithAllowedMediaRange(MediaRange.FromString("application/json"))
                    .WithModel(htmlDto)  // Model for 'text/html'
                    .WithMediaRangeModel(
                          MediaRange.FromString("application/json"), 
                          productDto); // Model for 'application/json';
        }
    }
}

虽然这可能会变得非常困惑。我的首选方法是在我的 Nancy 模块 Bootstrap 中“一次”设置我的错误处理,并让它捕获已知/预期的异常并使用适当的响应对象返回它们。

Bootstrap 配置的一个简单示例可能是:

public class MyNancyBootstrapper : DefaultNancyBootstrapper
{
    protected override void ApplicationStartup(
        TinyIoCContainer container, IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);

        // Register the custom exceptions handler.
        pipelines.OnError += (ctx, err) => HandleExceptions(err, ctx); ;
    }

    private static Response HandleExceptions(Exception err, NancyContext ctx)
    {
        var result = new Response();

        result.ReasonPhrase = err.Message;

        if (err is NotImplementedException)
        {
            result.StatusCode = HttpStatusCode.NotImplemented;
        }
        else if (err is UnauthorizedAccessException)
        {
            result.StatusCode = HttpStatusCode.Unauthorized;
        }
        else if (err is ArgumentException)
        {
            result.StatusCode = HttpStatusCode.BadRequest;
        }
        else
        {
            // An unexpected exception occurred!
            result.StatusCode = HttpStatusCode.InternalServerError;    
        }

        return result;
    }
}

使用它,您可以重构您的模块以简单地抛出将调用正确响应类型的适当异常。在这方面,您可以开始为您的 API 创建一套很好的标准。这方面的一个例子是:

public class ProductsModule : NancyModule
{
    public ProductsModule()
        : base("/products")
    {
        Get["/product/{productid}"] = _ => 
        {
            var request = this.Bind<ProductRequest>();

            var product = ProductRepository.GetById(request.ProductId);

            if (product == null)
            {
                throw new ArgumentException(
                    "Invalid product identifier.");
            }

            var user = UserRepository.GetCurrentUser();

            if (false == user.CanView(product))
            {
                throw new UnauthorizedAccessException(
                    "User has insufficient privileges.");
            }

            var productDto = CreateProductDto(product);

            var htmlDto = new {
              Product = productDto,
              RelatedProducts = GetRelatedProductsDto(product)
            };

            return Negotiate
                    .WithAllowedMediaRange(MediaRange.FromString("text/html"))
                    .WithAllowedMediaRange(MediaRange.FromString("application/json"))
                    .WithModel(htmlDto)  // Model for 'text/html'
                    .WithMediaRangeModel(
                          MediaRange.FromString("application/json"), 
                          productDto); // Model for 'application/json';
        }
    }
}

这对我来说感觉稍微干净一些,现在我在我的模块中引入了一组标准。 :)


您可以考虑做的其他事情(这在开发过程中特别有用)是将完整的异常报告附加到错误响应对象的内容结果中。

一个基本的例子是:

result.Contents = responseStream =>
    {
        string errorBody = string.Format(
            @"<html>
                <head>
                    <title>Exception report</title>
                </head>
                <body>
                    <h1>{0}</h1>
                    <p>{1}</p>
                </body>
              </html>",
            ex.Message,
            ex.StackTrace);

        // convert error to stream and copy to response stream
        var byteArray = Encoding.UTF8.GetBytes(errorBody);
        using (var errorStream = new MemoryStream(byteArray))
        {
            errorStream.CopyTo(responseStream);
        }
    }

同样,这只是一个非常基本的说明性示例,您必须确定它是否适合您的解决方案,然后再对其进行扩展。

关于c# - 如何得到 Nancy 谈判代表的回应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23352833/

相关文章:

http - 哪些 CDN 解决方案支持通过内容协商进行缓存?

c# - xUnit 进行 Json 文件读写测试

c# - Pdfium .NET SDK 中的打印功能

c# - 无法更改 Visual Studio 中的事件目标框架

C# ref 混淆

asp.net-mvc - 为自托管 NancyFx 复制 Razor View ?

compression - 用于自托管场景的 OWIN 压缩中间件

java - Spring API 中是否有更好的基于 Accept Header 的内容协商方法?

spring-mvc - 当客户端发送 Accept : */* 时,文本/html 优先于其他内容类型

c# - 遍历多个列表