public BindContext(RestRequest request, Snippet <MethodInfo> snippet) { Request = request.AssertNotNull(); Snippet = snippet.AssertNotNull(); Debug.EnsureBlankLine(); Debug.WriteLine("Binding request to {0}...", snippet.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces)); var asmUri = AsmResource.Uri ?? String.Empty; var typeUri = TypeResource.Uri ?? String.Empty; var methodUri = MethodResource.Uri ?? String.Empty; var uriTemplate = asmUri + typeUri + methodUri; ResourceTemplate = uriTemplate; Debug.WriteLine(" * Resource template: {0}", ResourceTemplate); var parsed1 = new Url(request.Resource).Parse(uriTemplate); var parsed2 = new Url(request.Resource + "/").Parse(uriTemplate); var parsed = parsed1 ?? parsed2; Resource = request.Resource; ParsedResource = parsed; Debug.WriteLine(" * Resource: {0}", Resource); if (parsed != null) { Debug.WriteLine(" + Resource matches the template"); var code = snippet.Member; HandlerCode = code; HandlerContext = new HandlerContext(request, parsed, code); Handler = () => { HandlerContext.Validate().AssertTrue(); var result = HandlerContext.Invoke(); var response = request.Response; var is_json = request["Content-Type"] == "application/json"; if (is_json) { response.Succeed(new Json(result)); } else { response.Succeed(result); } }; if (HandlerContext.Validate()) { Debug.WriteLine(" + Handler arguments are bound successfully"); var asmMethods = AsmResource.Allow; var typeMethods = TypeResource.Allow; var methodMethods = MethodResource.Allow; var allowedMethods = asmMethods & typeMethods & methodMethods; AllowedMethods = allowedMethods; Debug.WriteLine(" * Allowed HTTP methods: " + AllowedMethods); var allowMethod = false; allowMethod |= (request.Method == "GET" && ((allowedMethods & RestMethods.Get) != 0)); allowMethod |= (request.Method == "PUT" && ((allowedMethods & RestMethods.Put) != 0)); allowMethod |= (request.Method == "POST" && ((allowedMethods & RestMethods.Post) != 0)); allowMethod |= (request.Method == "MERGE" && ((allowedMethods & RestMethods.Merge) != 0)); allowMethod |= (request.Method == "DELETE" && ((allowedMethods & RestMethods.Delete) != 0)); Method = request.Method; Debug.WriteLine(" * HTTP method: " + Method); if (allowMethod) { Debug.WriteLine(" + HTTP method is allowed"); var authenticators = SnippetRegistry.Methods <AuthenticationFilterAttribute>(); var skipAuthentication = AsmResource.SkipAuthentication || TypeResource.SkipAuthentication || MethodResource.SkipAuthentication; var boundAuthenticators = authenticators.Select(authenticator => { Debug.WriteLine(" * Authorizing against {0}...", authenticator.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces)); return(new HandlerContext(request, parsed, authenticator.Member)); }).ToReadOnly(); var authenticated = skipAuthentication || boundAuthenticators.All(authenticator => authenticator.Validate() && Equals(authenticator.Invoke(), true)); if (authenticated) { Debug.WriteLine(" + Authentication successful"); var authorizers = SnippetRegistry.Methods <AuthorizationFilterAttribute>(); var skipAuthorization = AsmResource.SkipAuthorization || TypeResource.SkipAuthorization || MethodResource.SkipAuthorization; var boundAuthorizers = authorizers.Select(authorizer => { Debug.WriteLine(" * Authorizing against {0}...", authorizer.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces)); return(new HandlerContext(request, parsed, authorizer.Member)); }).ToReadOnly(); var authorized = skipAuthorization || boundAuthorizers.All(authorizer => authorizer.Validate() && Equals(authorizer.Invoke(), true)); if (authorized) { Debug.WriteLine(" + Authorization successful"); Debug.WriteLine(" * Bind successful"); Result = BindResult.Success; } else { Debug.WriteLine(" FAIL => Failed to authorize"); Result = BindResult.NotAuthorized; } } else { Debug.WriteLine(" FAIL => Failed to authenticate"); Result = BindResult.NotAuthenticated; } } else { Debug.WriteLine(" FAIL => HTTP method is not allowed"); Result = BindResult.MethodMismatch; } } else { Debug.WriteLine(" FAIL => Failed to bind handler arguments"); Result = BindResult.ArgsMismatch; } } else { Debug.WriteLine("FAIL => Resource doesn't match the template"); Result = BindResult.UrlMismatch; } }
public DispatchContext(RestRequest req) { Info.EnsureBlankLine(); Info.Write("Dispatching request "); var snippets = SnippetRegistry.Methods <RestResourceAttribute>(); Info.WriteLine("(across {0} handlers)...", snippets.Count()); BindResults = snippets.Select(snippet => new BindContext(req, snippet)).ToReadOnly(); // todo. maybe invent some novel way to do dispatch // so that "http://localhost/daas/ldab/Users?$filter=substringof('Bu', Name)" // doesn't get dispatched to "/{relativeFilePath}" and cause a crash with 400 // but rather becomes a 404 because it's just "/daas/ldap/{s_dataQuery}" with a typo var bestMatch = PickBestMatch(BindResults); if (bestMatch == null) { if (BindResults.Any(r => r.Result == BindResult.UrlMismatch)) { Result = DispatchResult.UrlMismatch; } if (BindResults.Any(r => r.Result == BindResult.ArgsMismatch)) { Result = DispatchResult.ArgsMismatch; } if (BindResults.Any(r => r.Result == BindResult.MethodMismatch)) { Result = DispatchResult.MethodNotAllowed; } if (BindResults.Any(r => r.Result == BindResult.NotAuthenticated)) { Result = DispatchResult.NotAuthenticated; } if (BindResults.Any(r => r.Result == BindResult.NotAuthorized)) { Result = DispatchResult.NotAuthorized; } if (BindResults.Count(r => r.Result == BindResult.Success) > 1) { Result = DispatchResult.Ambiguous; } } else { BestMatch = bestMatch; Result = (DispatchResult)Enum.Parse(typeof(DispatchResult), bestMatch.Result.ToString()); } Info.EnsureBlankLine(); Info.WriteLine("Dispatch summary: {0}", Result); Info.WriteLine(BindResults.Select(r => " * " + r.ToString()).StringJoin(Environment.NewLine)); Info.EnsureBlankLine(); if (Result != DispatchResult.Success) { Error.WriteLine("Dispatch has failed with status code {0}", Result.ToHttpStatusCode()); } else { Info.WriteLine("Bound handler: {0}", BestMatch.HandlerContext); } }