简体   繁体   中英

Custom routing in WCF data services

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!

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM