WCF Rest 4.0, динамическая маршрутизация и OutputCache - PullRequest
2 голосов
/ 01 июня 2011

У меня проблема с тем, чтобы OutputCaching работал с HttpContext.RewritePath для службы WCF 4.0 WebHttp.

Мой сервис локализован.Идея состоит в том, что вы называете URL следующим образом:

/languageCode/ServiceName/Method
e.g.
/en/MyService/GetItems

И он вернет результаты, локализованные на правильный язык.

Моя схема основана на этой статье .Идея состоит в том, чтобы создать производную от RouteBase, которая создает уникальный, «частный» маршрут к реальному сервису.Когда пользователь делает запрос, языковой код распаковывается с URL-адреса и устанавливается как культура для текущего потока, а затем HttpContext.RewritePath используется для загрузки фактического сервиса.

На всю жизнь яне могу понять, как работать OutputCaching в миксе.Я украсил свой метод обслуживания с помощью AspNetCacheProfile и вижу мой собственный вызов VaryByCustom.Однако, несмотря на получение дублированного результата от VaryByCustom, .NET все равно продолжает использовать мой метод обслуживания.

Множество кода ниже, извините за дамп, но я подозреваю, что все это уместно.


Как добавить маршрут в Global.asax.cs

RouteTable.Routes.Add(new CulturedServiceRoute(
    "devices", 
    new StructureMapServiceHostFactory(), 
    typeof(DeviceService)));

Переопределение VaryByCustom в Global.asax.cs:

public override string GetVaryByCustomString(
    HttpContext context, string custom)
{

    // This method gets called twice: Once for the initial request, then a 
    // second time for the rewritten URL. I only want it to be called once!

    if (custom == "XmlDataFreshness")
    {
        var outputString = String.Format("{0}|{1}|{2}", 
            XmlDataLoader.LastUpdatedTicks, 
            context.Request.RawUrl, 
            context.Request.HttpMethod);
        return outputString;
    }

    return base.GetVaryByCustomString(context, custom);
}

Это класс динамического маршрута службы.

public class CulturedServiceRoute : RouteBase, IRouteHandler
{
    private readonly string _virtualPath = null;
    private readonly ServiceRoute _innerServiceRoute = null;
    private readonly Route _innerRoute = null;

    public CulturedServiceRoute(
        string pathPrefix, 
        ServiceHostFactoryBase serviceHostFactory, 
        Type serviceType)
    {
        if (pathPrefix.IndexOf("{") >= 0)
        {
            throw new ArgumentException(
                "Path prefix cannot include route parameters.", 
                "pathPrefix");
        }
        if (!pathPrefix.StartsWith("/")) pathPrefix = "/" + pathPrefix;
        pathPrefix = "{culture}" + pathPrefix;

        _virtualPath = String.Format("Cultured/{0}/", serviceType.FullName);
        _innerServiceRoute = new ServiceRoute(
            _virtualPath, serviceHostFactory, serviceType);
        _innerRoute = new Route(pathPrefix, this);
    }

    public override RouteData GetRouteData(
        HttpContextBase httpContext)
    {
        return _innerRoute.GetRouteData(httpContext);
    }

    public override VirtualPathData GetVirtualPath(
        RequestContext requestContext, RouteValueDictionary values)
    {
        return null;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        // This method is called even if VaryByCustom 
        // returns a duplicate response!

        var culture = requestContext.RouteData.Values["culture"].ToString();
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = 
            CultureInfo.CreateSpecificCulture(ci.Name);

        requestContext.HttpContext.RewritePath("~/" + _virtualPath, true);
        return _innerServiceRoute.RouteHandler.GetHttpHandler(requestContext);
    }
}

Наконец, соответствующие части самой службы:

[ServiceContract]
[AspNetCompatibilityRequirements(
    RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class DeviceService
{
    [AspNetCacheProfile("MyCacheProfile")]
    [WebGet(UriTemplate = "")]
    public IEnumerable<DeviceListItemModel> GetDevices()
    {
        // This is called AFTER the first VaryByCustom override is called.
        // I'd expect it not to be called unless VaryByCustom changes!

        var devices =
            from d in _deviceRepository.GetAll()
            where d.ReleaseDate < DateTime.Now
            orderby d.Id descending
            select new DeviceListItemModel(d);

        return devices;
    }

ОБНОВЛЕНИЕ : Мой профиль кэша:

<caching>
  <outputCacheSettings>
    <outputCacheProfiles>
      <add name="MyCacheProfile" varyByCustom="XmlDataFreshness"
           varyByHeader="accept" varyByParam="*" location="Server"
           duration="3600" />
    </outputCacheProfiles>
  </outputCacheSettings>
</caching>

1 Ответ

0 голосов
/ 01 июня 2011

Хммм, кажется мне подходящим подходом. Правильно ли настроен профиль кеша? Вызывается ли varByCustom несколько раз и обязательно ли он возвращает тот же результат, когда кэш не нуждается в обновлении?

...