WCF 数据服务中的自定义路由

发布于 2024-12-20 07:40:55 字数 4117 浏览 0 评论 0原文

我需要为 WCF 数据服务创建一个自定义路由,其中​​包含必须提取以用于过滤数据的段。

示例:

http://mysample.net/mysamplesvc/client123/Users

我需要提取 client123从路线。看起来 Route 类可能提供类似的东西,但我不确定如何为数据服务实现 IRouteHandler。

这是正确的道路吗?周围有好的例子吗?

蒂亚!

更新:

我已经成功通过 IDispatchMessageInspector 中的一些自定义 URL 重写来实现我需要的解决方案。下面的代码是我最初的黑客攻击,需要大量清理。但是,它似乎正在发挥作用。如果有人发现任何明显错误的地方,请告诉我。

    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        HttpRequestMessageProperty httpmsg = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        ...Additional logic for handling Query formats in OData


        UriTemplate template = new UriTemplate("mysamplesvc/{ClientId}", true);

        Uri prefix = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority));
        Uri uri = new Uri(HttpContext.Current.Request.Url.AbsoluteUri);
        UriTemplateMatch results = template.Match(prefix, uri);

        if (results != null && !string.IsNullOrEmpty(results.BoundVariables["ClientId"]))
        {
            _clientId = results.BoundVariables["clientId"].ToString();
        }

        if (!string.IsNullOrEmpty(_clientId))
        {
            httpmsg.Headers.Add("ClientId", _clientId);
            rewriteRequest();
        }
        return null;
    }

    private void rewriteRequest()
    {
        if (HttpContext.Current != null && HttpContext.Current.Session != null)
        {
            if (WebOperationContext.Current.IncomingRequest.UriTemplateMatch != null)
            {
                Uri serviceUri = HttpContext.Current.Session["ServiceUri"] as Uri;
                Uri requestUri = null;

                UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;

                if (serviceUri == null)
                {
                    UriBuilder serviceUriBuilder = new UriBuilder(match.BaseUri);

                    serviceUri = serviceUriBuilder.Uri;
                    HttpContext.Current.Session["ServiceUri"] = serviceUri;
                }

                if (serviceUri != null)
                {
                    OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRootUri"] = serviceUri;

                    UriBuilder requestUriBuilder = new UriBuilder(match.RequestUri);
                    string path = string.Empty;
                    if (match.RelativePathSegments[0] == _clientId)
                    {
                        foreach (var seg in match.RelativePathSegments.Select((x, i) => new { Value = x, Index = i }))
                        {
                            if (seg.Index != 0)
                            {
                                path += "/";
                                path += seg.Value;
                            }
                        }
                    }
                    else
                    {
                        foreach (var seg in match.RelativePathSegments.Select((x, i) => new { Value = x, Index = i }))
                        {
                            path += "/";
                            path += seg.Value;
                        }
                    }

                    UriBuilder serviceUriBuilder = new UriBuilder(match.BaseUri + path);

                    // because we have overwritten the Root URI, we need to make sure the request URI shares the same host
                    // (sometimes we have request URI resolving to a different host, if there are firewall re-directs
                    serviceUriBuilder.Host = serviceUri.Host;

                    requestUri = serviceUriBuilder.Uri;
                    OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] = requestUri;
                    OperationContext.Current.IncomingMessageProperties["Via"] = requestUri;
                }
            }

        }
    }

谢谢大家!

I need to create a custom route for a WCF data service that contains a segment that must be extracted for use in filtering data.

Example:

http://mysample.net/mysamplesvc/client123/Users

I need to extract the client123 from the route. It looks like the Route class might provide something similar but I am not sure how to implement an IRouteHandler for a Data service.

Is this the correct path? Are there good examples around?

TIA!

UPDATE:

I have managed to achieve the solution I needed via some custom URL re-writing in the IDispatchMessageInspector. The below code is my initial hack and needs a bunch of clean-up. but, it appears to be working. If anybody sees anything galringly wrong, please let me know.

    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        HttpRequestMessageProperty httpmsg = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        ...Additional logic for handling Query formats in OData


        UriTemplate template = new UriTemplate("mysamplesvc/{ClientId}", true);

        Uri prefix = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority));
        Uri uri = new Uri(HttpContext.Current.Request.Url.AbsoluteUri);
        UriTemplateMatch results = template.Match(prefix, uri);

        if (results != null && !string.IsNullOrEmpty(results.BoundVariables["ClientId"]))
        {
            _clientId = results.BoundVariables["clientId"].ToString();
        }

        if (!string.IsNullOrEmpty(_clientId))
        {
            httpmsg.Headers.Add("ClientId", _clientId);
            rewriteRequest();
        }
        return null;
    }

    private void rewriteRequest()
    {
        if (HttpContext.Current != null && HttpContext.Current.Session != null)
        {
            if (WebOperationContext.Current.IncomingRequest.UriTemplateMatch != null)
            {
                Uri serviceUri = HttpContext.Current.Session["ServiceUri"] as Uri;
                Uri requestUri = null;

                UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;

                if (serviceUri == null)
                {
                    UriBuilder serviceUriBuilder = new UriBuilder(match.BaseUri);

                    serviceUri = serviceUriBuilder.Uri;
                    HttpContext.Current.Session["ServiceUri"] = serviceUri;
                }

                if (serviceUri != null)
                {
                    OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRootUri"] = serviceUri;

                    UriBuilder requestUriBuilder = new UriBuilder(match.RequestUri);
                    string path = string.Empty;
                    if (match.RelativePathSegments[0] == _clientId)
                    {
                        foreach (var seg in match.RelativePathSegments.Select((x, i) => new { Value = x, Index = i }))
                        {
                            if (seg.Index != 0)
                            {
                                path += "/";
                                path += seg.Value;
                            }
                        }
                    }
                    else
                    {
                        foreach (var seg in match.RelativePathSegments.Select((x, i) => new { Value = x, Index = i }))
                        {
                            path += "/";
                            path += seg.Value;
                        }
                    }

                    UriBuilder serviceUriBuilder = new UriBuilder(match.BaseUri + path);

                    // because we have overwritten the Root URI, we need to make sure the request URI shares the same host
                    // (sometimes we have request URI resolving to a different host, if there are firewall re-directs
                    serviceUriBuilder.Host = serviceUri.Host;

                    requestUri = serviceUriBuilder.Uri;
                    OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] = requestUri;
                    OperationContext.Current.IncomingMessageProperties["Via"] = requestUri;
                }
            }

        }
    }

Thanks all!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独享拥抱 2024-12-27 07:40:56

另一种选择是使用从以下位置获取的 IncomingWebRequestContext WebOperationContext传入请求。这将允许您直接访问 URI。缺点是您必须使用 Uri.Segments 进行解析,然后将另一段代码与 uri 格式相关联。

您的问题最终源于这样一个事实:尽管 WCF 声称不支持 REST,但它并不支持 REST。 REST应该是对由 URI 标识的资源进行的一组操作。相反,WCF 提供了一个“静态”端点和一组比真正的 REST 更类似于 old-skool XML/SOAP 的方法。

我个人发现 WCF 在处理作用于 URI/资源的 REST 服务时存在很大问题。坦率地说,它几乎没有提供任何价值,而且只是造成了阻碍。市面上有很多 REST 架构,其中许多都遭受着同样的限制。您可能会考虑使用 WCF 并找到支持您想要公开的格式的有效负载序列化库。

我目前最喜欢的是 protobuf-csharp-port,它支持 XML、JSON、协议缓冲区和 URI 编码消息。有一个简单介绍使用 protobuf-csharp-port 构建 REST 服务。尽管此示例也是一个服务端点,而不是基于资源的 REST,但底层序列化模式确实是您所追求的。

Another option is to use the IncomingWebRequestContext obtained from WebOperationContext.IncomingRequest. This will allow you directly access to the URI. The downside is that you will have to parse with Uri.Segments and will then have another piece of code tied to the uri-format.

You're problem ultimately stems from the fact that WCF, for all it's claims, does not support REST. REST should be a set of operations that take place on a resource identified by the URI. Instead, WCF provides a 'static' endpoint and a set of methods more similar to old-skool XML/SOAP than true REST.

I've personally found WCF to very problematic when dealing with REST services that act on a URI/resource. Frankly it provided little value and just got in the way. There are lots of REST architectures out there, many suffer this same limitation. You might consider bailing on WCF and find a payload serialization library that supports the formats you want to expose.

My current favorite is protobuf-csharp-port which supports XML, JSON, Protocol Buffers, and URI encoded messages. There is a brief introduction to building a REST service using protobuf-csharp-port. Although this sample is also a service-endpoint and not resource-based REST, the underlying serialization pattern is really what you're after.

嘿哥们儿 2024-12-27 07:40:56

我假设这是一个 MVC 项目

,我相信 MVC 中的典型路由是这样工作的:

//url looks like /controller/Details/42
public ViewResult Details(int id) {
  //do something
}

您可以像这样添加自定义路由:

routes.MapRoute(
           "my special little route", // Route name
           "customer/{Cid}/programs/{Pid}",
           new { controller = "customer", action = "Details" }
       );

所以视图看起来像:

//url looks like /customer/{21}/programs/42
public ViewResult Details(int Cid, int Pid) {
  //do something
}

所以理论上,您应该能够这样做您的 WCF 服务。除非我完全误解了你的意思,否则我会很乐意尝试更新我的答案

I'm assuming this is a MVC project

the typical routes in MVC, I believe, work like this:

//url looks like /controller/Details/42
public ViewResult Details(int id) {
  //do something
}

You can add custom routes like this:

routes.MapRoute(
           "my special little route", // Route name
           "customer/{Cid}/programs/{Pid}",
           new { controller = "customer", action = "Details" }
       );

so the view then looks like:

//url looks like /customer/{21}/programs/42
public ViewResult Details(int Cid, int Pid) {
  //do something
}

so you should, in theory, be able to do that to your WCF service. Unless I'm completely misunderstanding what you're saying at which I will happily try to update my answer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文