【问题标题】:WCF Rest weird routing behaviorWCF Rest 奇怪的路由行为
【发布时间】:2012-05-02 22:44:49
【问题描述】:

我有一个可用的资源 http://localhost/resource

我没有类似的路线 http://localhost/resource?name=john

但是当我尝试点击上面的 URI 时,我得到了http://localhost/resource 的结果。我期待的是 404。

知道为什么它忽略 ?name=john 并匹配 url 吗??

【问题讨论】:

    标签: wcf rest routing


    【解决方案1】:

    查询字符串参数是可选的,而不是地址的“正式”部分 - 它从方案到路径(通过主机和端口)。在很多情况下,您在地址http://something.com/path 处进行了一项操作,在操作代码中您查看查询字符串参数以做出一些决定。例如,下面的代码在查询字符串中查找可能会或可能不会传递的“格式”参数,并且所有请求(有或没有它)都正确路由到操作。

    public class StackOverflow_10422764
    {
        [DataContract(Name = "Person", Namespace = "")]
        public class Person
        {
            [DataMember]
            public string Name { get; set; }
            [DataMember]
            public int Age { get; set; }
        }
        [ServiceContract]
        public class Service
        {
            [WebGet(UriTemplate = "/NoQueryParams")]
            public Person GetPerson()
            {
                string format = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
                if (format == "xml")
                {
                    WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
                }
                else if (format == "json")
                {
                    WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
                }
    
                return new Person { Name = "Scooby Doo", Age = 10 };
            }
        }
        public static void Test()
        {
            string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
            WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
            host.Open();
            Console.WriteLine("Host opened");
    
            WebClient c = new WebClient();
            Console.WriteLine(c.DownloadString(baseAddress + "/NoQueryParams"));
    
            c = new WebClient();
            Console.WriteLine(c.DownloadString(baseAddress + "/NoQueryParams?format=json"));
    
            Console.Write("Press ENTER to close the host");
            Console.ReadLine();
            host.Close();
        }
    }
    

    cmets 后更新: 如果您想强制请求完全包含模板中指定的参数,那么您可以使用消息检查器之类的东西来验证它。

    public class StackOverflow_10422764
    {
        [DataContract(Name = "Person", Namespace = "")]
        public class Person
        {
            [DataMember]
            public string Name { get; set; }
            [DataMember]
            public int Age { get; set; }
        }
        [ServiceContract]
        public class Service
        {
            [WebGet(UriTemplate = "/NoQueryParams")]
            public Person GetPerson()
            {
                string format = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
                if (format == "xml")
                {
                    WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
                }
                else if (format == "json")
                {
                    WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
                }
    
                return new Person { Name = "Scooby Doo", Age = 10 };
            }
    
            [WebGet(UriTemplate = "/TwoQueryParam?x={x}&y={y}")]
            public int Add(int x, int y)
            {
                return x + y;
            }
        }
        public class MyForceQueryMatchBehavior : IEndpointBehavior, IDispatchMessageInspector
        {
            #region IEndpointBehavior Members
    
            public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
            {
            }
    
            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
            }
    
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
            {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
            }
    
            public void Validate(ServiceEndpoint endpoint)
            {
            }
    
            #endregion
    
            #region IDispatchMessageInspector Members
    
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                UriTemplateMatch uriTemplateMatch = (UriTemplateMatch)request.Properties["UriTemplateMatchResults"];
    
                // TODO: may need to deal with the case of implicit UriTemplates, or if you want to allow for some query parameters to be ommitted
    
                List<string> uriTemplateQueryVariables = uriTemplateMatch.Template.QueryValueVariableNames.Select(x => x.ToLowerInvariant()).ToList();
                List<string> requestQueryVariables = uriTemplateMatch.QueryParameters.Keys.OfType<string>().Select(x => x.ToLowerInvariant()).ToList();
                if (uriTemplateQueryVariables.Count != requestQueryVariables.Count || uriTemplateQueryVariables.Except(requestQueryVariables).Count() > 0)
                {
                    throw new WebFaultException(HttpStatusCode.NotFound);
                }
    
                return null;
            }
    
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
            }
    
            #endregion
        }
        static void SendRequest(string uri)
        {
            Console.WriteLine("Request to {0}", uri);
            WebClient c = new WebClient();
            try
            {
                Console.WriteLine("  {0}", c.DownloadString(uri));
            }
            catch (WebException e)
            {
                HttpWebResponse resp = e.Response as HttpWebResponse;
                Console.WriteLine("  ERROR => {0}", resp.StatusCode);
            }
        }
        public static void Test()
        {
            string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
            ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
            ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
            endpoint.Behaviors.Add(new WebHttpBehavior());
            endpoint.Behaviors.Add(new MyForceQueryMatchBehavior());
            host.Open();
            Console.WriteLine("Host opened");
    
            SendRequest(baseAddress + "/NoQueryParams");
            SendRequest(baseAddress + "/NoQueryParams?format=json");
            SendRequest(baseAddress + "/TwoQueryParam?x=7&y=9&z=other");
            SendRequest(baseAddress + "/TwoQueryParam?x=7&y=9");
    
            Console.Write("Press ENTER to close the host");
            Console.ReadLine();
            host.Close();
        }
    }
    

    【讨论】:

    • 我明白这一点...但是是否可以对其进行配置,以便在 URI 模板上没有 100% 匹配时响应某种错误?
    • 只是为了澄清“查询字符串参数是可选的,而不是“官方”地址的一部分”。您的意思是 WCF REST 框架不将查询字符串参数视为“地址的一部分”。但是,根据 URI 规范 ietf.org/rfc/rfc3986.txt 查询字符串参数是资源标识的一部分。
    • @DarrelMiller 感谢您的评论。由于它的行为方式,我有点重新思考我的 REST 概念。有趣的是,即使是 FB api 也能像我的 api 一样工作。但是我想这对于 MVC 应用程序来说是一个很好的行为。但是对于一个安静的 api,我在想 1 URI 模式 = 资源。我想知道 OpenRasta 和 ServiceStack 如何处理这个
    • @Perpetualcoder,您可以使用消息检查器之类的东西来做到这一点,它将模板中的查询变量与请求中的变量进行比较,如果它们不匹配则失败。我也用这个选项更新了答案。
    猜你喜欢
    • 1970-01-01
    • 2014-10-23
    • 2017-09-01
    • 2016-09-25
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多