Fixed razor view as its compilation fails due to changed apis

Updated PageGenerator to consider the modified Views folder paths
More restructuring of files
This commit is contained in:
Kiran Challa 2015-09-03 09:34:19 -07:00
parent 9044aeff47
commit 1a708b8479
33 changed files with 138 additions and 121 deletions

View File

@ -11,7 +11,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WelcomePageSample", "sample
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics", "src\Microsoft.AspNet.Diagnostics\Microsoft.AspNet.Diagnostics.xproj", "{68A1F0E1-ECCE-46D1-B20F-C43EE5B097DE}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics", "src\Microsoft.AspNet.Diagnostics\Microsoft.AspNet.Diagnostics.xproj", "{68A1F0E1-ECCE-46D1-B20F-C43EE5B097DE}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ErrorPageSample", "samples\ErrorPageSample\ErrorPageSample.xproj", "{589AC17F-9455-4764-8F82-FCD2AE58DA14}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DeveloperExceptionPageSample", "samples\ErrorPageSample\DeveloperExceptionPageSample.xproj", "{589AC17F-9455-4764-8F82-FCD2AE58DA14}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PageGenerator", "src\PageGenerator\PageGenerator.xproj", "{4D4A785A-ECB9-4916-A88F-0FD306EE3B74}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PageGenerator", "src\PageGenerator\PageGenerator.xproj", "{4D4A785A-ECB9-4916-A88F-0FD306EE3B74}"
EndProject EndProject
@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2AF90579-B
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Tests", "test\Microsoft.AspNet.Diagnostics.Tests\Microsoft.AspNet.Diagnostics.Tests.xproj", "{994351B4-7B2A-4139-8B72-72C5BB5CC618}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Tests", "test\Microsoft.AspNet.Diagnostics.Tests\Microsoft.AspNet.Diagnostics.Tests.xproj", "{994351B4-7B2A-4139-8B72-72C5BB5CC618}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ErrorHandlerSample", "samples\ErrorHandlerSample\ErrorHandlerSample.xproj", "{427CDB36-78B0-4583-9EBC-7F283DE60355}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ExceptionHandlerSample", "samples\ErrorHandlerSample\ExceptionHandlerSample.xproj", "{427CDB36-78B0-4583-9EBC-7F283DE60355}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Entity", "src\Microsoft.AspNet.Diagnostics.Entity\Microsoft.AspNet.Diagnostics.Entity.xproj", "{4F5A6A72-FFE4-49C4-B4C6-58132CFCB9FE}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Diagnostics.Entity", "src\Microsoft.AspNet.Diagnostics.Entity\Microsoft.AspNet.Diagnostics.Entity.xproj", "{4F5A6A72-FFE4-49C4-B4C6-58132CFCB9FE}"
EndProject EndProject

View File

@ -5,7 +5,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
namespace ErrorHandlerSample namespace ExceptionHandlerSample
{ {
public class Startup public class Startup
{ {
@ -35,7 +35,7 @@ namespace ErrorHandlerSample
}); });
// We could also configure it to re-execute the request on the normal pipeline with a different path. // We could also configure it to re-execute the request on the normal pipeline with a different path.
// app.UseErrorHandler("/error.html"); // app.UseExceptionHandler("/error.html");
// The broken section of our application. // The broken section of our application.
app.Map("/throw", throwApp => app.Map("/throw", throwApp =>

View File

@ -1,7 +1,7 @@
using System; using System;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
namespace ErrorPageSample namespace DeveloperExceptionPageSample
{ {
public class Startup public class Startup
{ {

View File

@ -1,11 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Diagnostics
{
internal static class Constants
{
internal const string HostAppMode = "host.AppMode";
internal const string DevMode = "development";
}
}

View File

@ -11,28 +11,28 @@ namespace Microsoft.AspNet.Builder
/// </summary> /// </summary>
public static class ErrorPageExtensions public static class ErrorPageExtensions
{ {
///// <summary> /// <summary>
///// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses. /// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses.
///// Full error details are only displayed by default if 'host.AppMode' is set to 'development' in the IApplicationBuilder.Properties. /// Full error details are only displayed by default if 'host.AppMode' is set to 'development' in the IApplicationBuilder.Properties.
///// </summary> /// </summary>
///// <param name="builder"></param> /// <param name="builder"></param>
///// <returns></returns> /// <returns></returns>
//public static IApplicationBuilder UseErrorPage([NotNull] this IApplicationBuilder builder) public static IApplicationBuilder UseErrorPage([NotNull] this IApplicationBuilder builder)
//{ {
// return builder.UseErrorPage(new ErrorPageOptions()); return builder.UseErrorPage(new ErrorPageOptions());
//} }
///// <summary> /// <summary>
///// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses. /// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses.
///// Full error details are only displayed by default if 'host.AppMode' is set to 'development' in the IApplicationBuilder.Properties. /// Full error details are only displayed by default if 'host.AppMode' is set to 'development' in the IApplicationBuilder.Properties.
///// </summary> /// </summary>
///// <param name="builder"></param> /// <param name="builder"></param>
///// <param name="options"></param> /// <param name="options"></param>
///// <returns></returns> /// <returns></returns>
//public static IApplicationBuilder UseErrorPage([NotNull] this IApplicationBuilder builder, ErrorPageOptions options) public static IApplicationBuilder UseErrorPage([NotNull] this IApplicationBuilder builder, ErrorPageOptions options)
//{ {
// return builder.UseMiddleware<DeveloperExceptionPageMiddleware>(options); return builder.UseMiddleware<DeveloperExceptionPageMiddleware>(options);
//} }
/// <summary> /// <summary>
/// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses. /// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses.

View File

@ -34,4 +34,33 @@ namespace Microsoft.AspNet.Diagnostics
/// </remarks> /// </remarks>
public IFileProvider FileProvider { get; set; } public IFileProvider FileProvider { get; set; }
} }
/// <summary>
/// Options for the DeveloperExceptionPageMiddleware.
/// </summary>
public class DeveloperExceptionPageOptions
{
/// <summary>
/// Create an instance with the default options settings.
/// </summary>
public DeveloperExceptionPageOptions()
{
SourceCodeLineCount = 6;
}
/// <summary>
/// Determines how many lines of code to include before and after the line of code
/// present in an exception's stack frame. Only applies when symbols are available and
/// source code referenced by the exception stack trace is present on the server.
/// </summary>
public int SourceCodeLineCount { get; set; }
/// <summary>
/// Provides files containing source code used to display contextual information of an exception.
/// </summary>
/// <remarks>
/// If <c>null</c> <see cref="DeveloperExceptionPageMiddleware" /> will use a <see cref="PhysicalFileProvider"/>.
/// </remarks>
public IFileProvider FileProvider { get; set; }
}
} }

View File

@ -62,10 +62,10 @@ using System.Globalization
#line default #line default
#line hidden #line hidden
WriteLiteral("</h2>\r\n <p><a"); WriteLiteral("</h2>\r\n <p><a");
WriteAttribute("href", Tuple.Create(" href=\"", 767), Tuple.Create("\"", 858), WriteAttribute("href", Tuple.Create(" href=\"", 763), Tuple.Create("\"", 854),
Tuple.Create(Tuple.Create("", 774), Tuple.Create<System.Object, System.Int32>(Request.PathBase, 774), false), Tuple.Create(Tuple.Create("", 770), Tuple.Create<System.Object, System.Int32>(Request.PathBase, 770), false),
Tuple.Create(Tuple.Create("", 791), Tuple.Create<System.Object, System.Int32>(Request.Path, 791), false), Tuple.Create(Tuple.Create("", 804), Tuple.Create("?error=", 804), true), Tuple.Create(Tuple.Create("", 787), Tuple.Create<System.Object, System.Int32>(Request.Path, 787), false), Tuple.Create(Tuple.Create("", 800), Tuple.Create("?error=", 800), true),
Tuple.Create(Tuple.Create("", 811), Tuple.Create<System.Object, System.Int32>(Resources.DiagnosticsPageHtml_TestErrorMessage, 811), false)); Tuple.Create(Tuple.Create("", 807), Tuple.Create<System.Object, System.Int32>(Resources.DiagnosticsPageHtml_TestErrorMessage, 807), false));
WriteLiteral(">throw InvalidOperationException</a></p>\r\n </div>\r\n</body>\r\n</html>\r\n"); WriteLiteral(">throw InvalidOperationException</a></p>\r\n </div>\r\n</body>\r\n</html>\r\n");
} }
#pragma warning restore 1998 #pragma warning restore 1998

View File

@ -2,7 +2,7 @@
@using System.Globalization @using System.Globalization
@{ @{
Response.ContentType = "text/html"; Response.ContentType = "text/html";
string error = Request.Query.Get("error"); string error = Request.Query["error"];
if (!string.IsNullOrWhiteSpace(error)) if (!string.IsNullOrWhiteSpace(error))
{ {
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "User requested error '{0}'", error)); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "User requested error '{0}'", error));

View File

@ -9,40 +9,40 @@ namespace Microsoft.AspNet.Builder
{ {
public static class ExceptionHandlerExtensions public static class ExceptionHandlerExtensions
{ {
///// <summary> /// <summary>
///// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request. /// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request.
///// The request will not be re-executed if the response has already started. /// The request will not be re-executed if the response has already started.
///// </summary> /// </summary>
///// <param name="app"></param> /// <param name="app"></param>
///// <param name="errorHandlingPath"></param> /// <param name="errorHandlingPath"></param>
///// <returns></returns> /// <returns></returns>
//public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, string errorHandlingPath) public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, string errorHandlingPath)
//{ {
// var options = new ErrorHandlerOptions() var options = new ExceptionHandlerOptions()
// { {
// ErrorHandlingPath = new PathString(errorHandlingPath) ExceptionHandlingPath = new PathString(errorHandlingPath)
// }; };
// return app.UseMiddleware<ErrorHandlerMiddleware>(options); return app.UseMiddleware<ExceptionHandlerMiddleware>(options);
//} }
///// <summary> /// <summary>
///// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline. /// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.
///// The request will not be re-executed if the response has already started. /// The request will not be re-executed if the response has already started.
///// </summary> /// </summary>
///// <param name="app"></param> /// <param name="app"></param>
///// <param name="configure"></param> /// <param name="configure"></param>
///// <returns></returns> /// <returns></returns>
//public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure) public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure)
//{ {
// var subAppBuilder = app.New(); var subAppBuilder = app.New();
// configure(subAppBuilder); configure(subAppBuilder);
// var errorPipeline = subAppBuilder.Build(); var errorPipeline = subAppBuilder.Build();
// var options = new ErrorHandlerOptions() var options = new ExceptionHandlerOptions()
// { {
// ErrorHandler = errorPipeline ExceptionHandler = errorPipeline
// }; };
// return app.UseMiddleware<ErrorHandlerMiddleware>(options); return app.UseMiddleware<ExceptionHandlerMiddleware>(options);
//} }
/// <summary> /// <summary>
/// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request. /// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request.
@ -53,9 +53,9 @@ namespace Microsoft.AspNet.Builder
/// <returns></returns> /// <returns></returns>
public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, string errorHandlingPath) public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, string errorHandlingPath)
{ {
var options = new ErrorHandlerOptions() var options = new ExceptionHandlerOptions()
{ {
ErrorHandlingPath = new PathString(errorHandlingPath) ExceptionHandlingPath = new PathString(errorHandlingPath)
}; };
return app.UseMiddleware<ExceptionHandlerMiddleware>(options); return app.UseMiddleware<ExceptionHandlerMiddleware>(options);
} }
@ -72,9 +72,9 @@ namespace Microsoft.AspNet.Builder
var subAppBuilder = app.New(); var subAppBuilder = app.New();
configure(subAppBuilder); configure(subAppBuilder);
var errorPipeline = subAppBuilder.Build(); var errorPipeline = subAppBuilder.Build();
var options = new ErrorHandlerOptions() var options = new ExceptionHandlerOptions()
{ {
ErrorHandler = errorPipeline ExceptionHandler = errorPipeline
}; };
return app.UseMiddleware<ExceptionHandlerMiddleware>(options); return app.UseMiddleware<ExceptionHandlerMiddleware>(options);
} }

View File

@ -14,18 +14,18 @@ namespace Microsoft.AspNet.Diagnostics
public class ExceptionHandlerMiddleware public class ExceptionHandlerMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly ErrorHandlerOptions _options; private readonly ExceptionHandlerOptions _options;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly Func<object, Task> _clearCacheHeadersDelegate; private readonly Func<object, Task> _clearCacheHeadersDelegate;
public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ErrorHandlerOptions options) public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ExceptionHandlerOptions options)
{ {
_next = next; _next = next;
_options = options; _options = options;
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>(); _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
if (_options.ErrorHandler == null) if (_options.ExceptionHandler == null)
{ {
_options.ErrorHandler = _next; _options.ExceptionHandler = _next;
} }
_clearCacheHeadersDelegate = ClearCacheHeaders; _clearCacheHeadersDelegate = ClearCacheHeaders;
} }
@ -47,17 +47,17 @@ namespace Microsoft.AspNet.Diagnostics
} }
PathString originalPath = context.Request.Path; PathString originalPath = context.Request.Path;
if (_options.ErrorHandlingPath.HasValue) if (_options.ExceptionHandlingPath.HasValue)
{ {
context.Request.Path = _options.ErrorHandlingPath; context.Request.Path = _options.ExceptionHandlingPath;
} }
try try
{ {
var errorHandlerFeature = new ExceptionHandlerFeature() var exceptionHandlerFeature = new ExceptionHandlerFeature()
{ {
Error = ex, Error = ex,
}; };
context.Features.Set<IExceptionHandlerFeature>(errorHandlerFeature); context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
context.Response.Headers.Clear(); context.Response.Headers.Clear();
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response); context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Diagnostics
context.Response.Body.SetLength(0); context.Response.Body.SetLength(0);
} }
await _options.ErrorHandler(context); await _options.ExceptionHandler(context);
// TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws. // TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
return; return;
} }

View File

@ -6,10 +6,10 @@ using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Diagnostics namespace Microsoft.AspNet.Diagnostics
{ {
public class ErrorHandlerOptions public class ExceptionHandlerOptions
{ {
public PathString ErrorHandlingPath { get; set; } public PathString ExceptionHandlingPath { get; set; }
public RequestDelegate ErrorHandler { get; set; } public RequestDelegate ExceptionHandler { get; set; }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"version": "1.0.0-*", "version": "1.0.0-*",
"description": "ASP.NET 5 Middleware for error handling, error pages, and diagnostics information.", "description": "ASP.NET 5 Middleware for exception handling, exception display pages, and diagnostics information.",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/aspnet/diagnostics" "url": "git://github.com/aspnet/diagnostics"

View File

@ -35,20 +35,31 @@ namespace PageGenerator
"Unable to open library {0}. Is it spelled correctly and listed as a dependency in project.json?", "Unable to open library {0}. Is it spelled correctly and listed as a dependency in project.json?",
args[0])); args[0]));
} }
var viewBasePath = Path.Combine(Path.GetDirectoryName(diagnosticsLibInfo.Path), "Views");
Console.WriteLine("Generating code files for views in {0}", viewBasePath); var viewDirectories = Directory.EnumerateDirectories(
Console.WriteLine(); Path.GetDirectoryName(diagnosticsLibInfo.Path), "Views", SearchOption.AllDirectories);
var cshtmlFiles = GetCshtmlFiles(viewBasePath);
var fileCount = 0; var fileCount = 0;
foreach (var fileName in cshtmlFiles) foreach (var viewDir in viewDirectories)
{ {
Console.WriteLine(" Generating code file for view {0}...", Path.GetFileName(fileName)); Console.WriteLine();
GenerateCodeFile(fileName, string.Format("{0}.Views", args[0])); Console.WriteLine(" Generating code files for views in {0}", viewDir);
Console.WriteLine(" Done!");
fileCount++; var cshtmlFiles = Directory.EnumerateFiles(viewDir, "*.cshtml");
if (!cshtmlFiles.Any())
{
Console.WriteLine(" No .cshtml files were found.");
continue;
}
foreach (var fileName in cshtmlFiles)
{
Console.WriteLine(" Generating code file for view {0}...", Path.GetFileName(fileName));
GenerateCodeFile(fileName, string.Format("{0}.Views", args[0]));
Console.WriteLine(" Done!");
fileCount++;
}
} }
Console.WriteLine(); Console.WriteLine();
@ -56,16 +67,6 @@ namespace PageGenerator
Console.WriteLine(); Console.WriteLine();
} }
private static IEnumerable<string> GetCshtmlFiles(string path)
{
if (!Directory.Exists(path))
{
throw new ArgumentException("path");
}
return Directory.EnumerateFiles(path, "*.cshtml");
}
private static void GenerateCodeFile(string cshtmlFilePath, string rootNamespace) private static void GenerateCodeFile(string cshtmlFilePath, string rootNamespace)
{ {
var basePath = Path.GetDirectoryName(cshtmlFilePath); var basePath = Path.GetDirectoryName(cshtmlFilePath);
@ -111,7 +112,7 @@ namespace PageGenerator
} }
var includeFileName = source.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length)); var includeFileName = source.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length));
includeFileName = SanitizeFileName(includeFileName); includeFileName = SanitizeFileName(includeFileName);
Console.WriteLine(" Inlining file {0}", includeFileName); Console.WriteLine(" Inlining file {0}", includeFileName);
var replacement = File.ReadAllText(Path.Combine(basePath, includeFileName)).Replace("\"", "\\\"").Replace("\r\n", "\\r\\n"); var replacement = File.ReadAllText(Path.Combine(basePath, includeFileName)).Replace("\"", "\\\"").Replace("\r\n", "\\r\\n");
source = source.Substring(0, startIndex) + replacement + source.Substring(endIndex + endMatch.Length); source = source.Substring(0, startIndex) + replacement + source.Substring(endIndex + endMatch.Length);
startIndex = startIndex + replacement.Length; startIndex = startIndex + replacement.Length;

View File

@ -19,7 +19,7 @@ using Microsoft.AspNet.Testing;
namespace Microsoft.AspNet.Diagnostics namespace Microsoft.AspNet.Diagnostics
{ {
public class ErrorPageMiddlewareTest public class DeveloperExceptionPageMiddlewareTest
{ {
public static TheoryData RelativePathsData public static TheoryData RelativePathsData
{ {
@ -124,7 +124,7 @@ namespace Microsoft.AspNet.Diagnostics
// Arrange // Arrange
var provider = new EmbeddedFileProvider( var provider = new EmbeddedFileProvider(
GetType().GetTypeInfo().Assembly, GetType().GetTypeInfo().Assembly,
baseNamespace: $"{typeof(ErrorPageMiddlewareTest).GetTypeInfo().Assembly.GetName().Name}.Resources"); baseNamespace: $"{typeof(DeveloperExceptionPageMiddlewareTest).GetTypeInfo().Assembly.GetName().Name}.Resources");
// Act // Act
var middleware = GetErrorPageMiddleware(provider); var middleware = GetErrorPageMiddleware(provider);

View File

@ -6,17 +6,15 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.TestHost; using Microsoft.AspNet.TestHost;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Diagnostics namespace Microsoft.AspNet.Diagnostics
{ {
public class ErrorHandlerTest public class ExceptionHandlerTest
{ {
[Theory] [Theory]
[InlineData(HttpStatusCode.NotFound)] [InlineData(HttpStatusCode.NotFound)]