示例#1
0
        public HandlerContext(RestRequest request, ReadOnlyDictionary<String, String> env, MethodInfo code)
        {
            Request = request.AssertNotNull();
            Env = env.AssertNotNull();
            Code = code.AssertNotNull();

            // todo:
            // 1) allow to return void, json, string, byte[], stream and any other object (the latter will then be serialized to json)
            // 2) allow non-statics
            // 3) allow RestRequest, RestResponse, RestContext, RestHints, Query, RequestHeaders, ResponseHeaders, Cookies, dynamic/Json (Data), Stream (output stream), TextWriter (response)
            // 4) allow config types
            // 5) deserialize when binding
            // 6) bind to fields as well
            // 7) comprehensive logging
            // 8) try to bind everything, rather than stop at first error

            Debug.EnsureBlankLine();
            Debug.WriteLine("    * Env: {0}", Env.Select(kvp => String.Format("{0} = {1}", kvp.Key, kvp.Value)).StringJoin());
            Debug.WriteLine("    * Query: {0}", request.Query.Select(kvp => String.Format("{0} = {1}", kvp.Key, (String)kvp.Value)).StringJoin().Fluent(s => s.IsEmpty() ? "<empty>" : s));
            Debug.WriteLine("    * Data: {0}", ((Json)request.Data).ToCompactString().Fluent(s => s.IsEmpty() ? "<empty>" : s));
            // todo. after that log:
            // Write("    * field foo <= ")
            // do something, then write either:
            // 1) WriteLine("Data.foo") or
            // 2) WriteLine("FAIL") or even
            // 3) exception.... [will be traced by RestGateway]
        }
示例#2
0
 public RestContext(HttpContext native)
 {
     _native = native;
     _req = new RestRequest(this);
     _resp = new RestResponse(this);
     _hints = new RestHints(this);
     native.Items["XenoGears.RestContext"] = this;
 }
示例#3
0
        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);
        }
示例#4
0
        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;
            }
        }