6

I'm building a Request/Acknowledge/Poll style REST service with NServiceBus underneath to manage queue processing. I want to give the client a URI to poll for updates.

Therefore I want to return a location header element in my web service as part of the acknowledgement. I can see that it is possible to do this:

return new HttpResult(response, HttpStatusCode.Accepted)
{
    Location = base.Request.AbsoluteUri.CombineWith(response.Reference)
}

But for a Url such as: http://localhost:54567/approvals/?message=test, which creates a new message (I know I should probably just use a POST), the location will be returned as: http://localhost:54567/approvals/?message=test/8f0ab1c1a2ca46f8a98b75330fd3ac5c.

The ServiceStack request doesn't expose the Uri fragments, only the AbsouteUri. This means that I need to access the original request. I want this to work regardless of whether this is running in IIS or in a self hosted process. The closest I can come up with is the following, but it seems very clunky:

var reference = Guid.NewGuid().ToString("N");
var response = new ApprovalResponse { Reference = reference };

var httpRequest = ((System.Web.HttpRequest)base.Request.OriginalRequest).Url;
var baseUri = new Uri(String.Concat(httpRequest.Scheme, Uri.SchemeDelimiter, httpRequest.Host, ":", httpRequest.Port));
var uri = new Uri(baseUri, string.Format("/approvals/{0}", reference));

return new HttpResult(response, HttpStatusCode.Accepted)
{
    Location = uri.ToString()
};

This now returns: http://localhost:55847/approvals/8f0ab1c1a2ca46f8a98b75330fd3ac5c

Any suggestions? Does this work regardless of how ServiceStack is hosted? I'm a little scared of the System.Web.HttpRequest casting in a self hosted process. Is this code safe?

4

1 回答 1

5

Reverse Routing

If you're trying to build urls for ServiceStack services you can use the RequestDto.ToGetUrl() and RequestDto.ToAbsoluteUri() to build relative and absolute urls as seen in this earlier question on Reverse Routing. e.g:

[Route("/reqstars/search", "GET")]
[Route("/reqstars/aged/{Age}")]
public class SearchReqstars : IReturn<ReqstarsResponse>
{
    public int? Age { get; set; }
}

var relativeUrl = new SearchReqstars { Age = 20 }.ToUrl("GET");
var absoluteUrl = HostContext.Config.WebHostUrl.CombineWith(relativeUrl);

relativeUrl.Print(); //=  /reqstars/aged/20
absoluteUrl.Print(); //=  http://www.myhost.com/reqstars/aged/20

For creating Urls for other 3rd Party APIs look at the Http Utils wiki for example extension methods that can help, e.g:

var url ="http://api.twitter.com/user_timeline.json?screen_name={0}".Fmt(name);
if (sinceId != null)
    url = url.AddQueryParam("since_id", sinceId);
if (maxId != null)
    url = url.AddQueryParam("max_id", maxId);

var tweets = url.GetJsonFromUrl()
    .FromJson<List<Tweet>>();

You can also use the QueryStringSerializer to serialize a number of different collection types, e.g:

//Typed POCO
var url = "http://example.org/login?" + QueryStringSerializer.SerializeToString(
   new Login { Username="mythz", Password="password" });

//Anonymous type
var url = "http://example.org/login?" + QueryStringSerializer.SerializeToString(
   new { Username="mythz", Password="password" });

//string Dictionary
var url = "http://example.org/login?" + QueryStringSerializer.SerializeToString(
  new Dictionary<string,string> {{"Username","mythz"}, {"Password","password"}});

You can also serialize the built-in NameValueCollection.ToFormUrlEncoded() extension, e.g:

var url = "http://example.org/login?" + new NameValueCollection { 
    {"Username","mythz"}, {"Password","password"} }.ToFormUrlEncoded();
于 2013-04-10T15:55:56.830 回答