servicestack在uri中传递斜线

时间:2014-02-09 10:51:00

标签: escaping servicestack uri

我正在使用servicestack构建一个web api,通过Web提供一些旧数据。不幸的是,数据模式不适合ServiceStack.Ormlite的标准使用。例如,没有主键,并且在一个表上(非唯一)键实际上可以包含任何字符。在我的示例数据库中,键中有“/”字符。

因此,当使用此路由“/ api / objects / {objectCode}”从web api请求资源时,如果所需的objectCode为1/1,则路由应为“/ api / objects / 1/1”,但此结果在页面中未找到异常。当我在以下路由上请求资源时,“/ api / objects / {objectCode} / subObjects”

  1. 有谁知道我应该如何解决这个问题?我应该围绕这个问题进行设计还是我可以做些什么来允许它?

  2. 另外,理想情况下我希望能够传递这些objectCodes的数组,但我不能保证objectCode值中不会有,,所以分隔字符会出现在资源代码和ServiceStack的分隔符解析,没有?

  3. 我已经搜索过线索,但只发现有人询问查询字符串中的编码而不是URI本身。

1 个答案:

答案 0 :(得分:4)

不幸的是,在URL中使用正斜杠,逗号和其他不常见的字符会产生不希望的结果,显然在您的路线中不易使用。但是可以通过编码Id值来解决这个问题。

我们可以使用ServiceStack极大的过滤灵活性,使值的编码和解码对您现有的ServiceStack服务完全透明。

透明编码/解码复杂Id值

此方法将使用“请求和响应”过滤器属性:

  • 请求过滤器将负责解码任何编码的Id值。
  • 响应过滤器将负责编码普通Id值。

因此Id值将始终在传输过程中进行编码,并在服务器端服务实现中完全解码。

Full Source Code Here

在我的例子中,我使用了base64编码。但您可以将其替换为您想要的任何编码。例如,您可以选择简单地将正斜杠转换为下划线。

执行编码和解码的属性:

public class UsesEncodedAttribute : Attribute, IHasRequestFilter, IHasResponseFilter
{
    IHasRequestFilter IHasRequestFilter.Copy()
    {
        return this;
    }

    IHasResponseFilter IHasResponseFilter.Copy()
    {
        return this;
    }

    public void RequestFilter(IRequest req, IResponse res, object requestDto)
    {
        // Decode the properties on the requestDto having the EncodedId attribute
        var type = requestDto.GetType();
        var properties = type.GetPublicProperties();
        foreach(var p in properties)
        {
            // Find the property marked with EncodedId that is of type string, that can be read and written to
            if(!p.HasAttribute<EncodedIdAttribute>() || p.PropertyType != typeof(string) || !p.CanRead || !p.CanWrite)
                continue;

            // Get the encoded value
            string encodedValue = p.GetValue(requestDto, null) as string;
            if(encodedValue != null)
            {
                // Decode the value from base64
                string decodedValue = Encoding.UTF8.GetString(Convert.FromBase64String(encodedValue));

                // Set the value to decoded string
                p.SetValue(requestDto, decodedValue, null);
            }
        }
    }

    public void ResponseFilter(IRequest req, IResponse res, object response)
    {
        // Encode properties on the response having the EncodedId attribute
        var type = response.GetType();
        var properties = type.GetPublicProperties();
        foreach(var p in properties)
        {
            // Find the property marked with EncodedId that is of type string, that can be read and written to
            if(!p.HasAttribute<EncodedIdAttribute>() || p.PropertyType != typeof(string) || !p.CanRead || !p.CanWrite)
                continue;

            // Get the decoded value
            string decodedValue = p.GetValue(response, null) as string;
            if(decodedValue != null)
            {
                // Encode the value to base64
                string encodedValue = Convert.ToBase64String(decodedValue.ToUtf8Bytes());

                // Set the value to decoded string
                p.SetValue(response, encodedValue, null);
            }
        }
    }

    // The lowest priority means it will run first, before your other filters
    public int Priority { get { return int.MinValue; } }
}

标记需要编码/解码的属性的简单属性:

public class EncodedIdAttribute : Attribute { }

用法:

只需在您的请求中添加[UsesEncodedAttribute]属性,并回复具有已编码Id值的DTO。然后使用[EncodedId]属性标记需要编码/解码的属性。 请注意,您可以使用此属性标记多个属性,如果您有外键,则非常有用。

[UsesEncodedAttribute]
[Route("/Object/{Id}","GET")]
public class GetObjectWithComplexIdRequest : IReturn<ObjectWithComplexIdResponse>
{
    [EncodedId]
    public string Id { get; set; }
}

[UsesEncodedAttribute]
public class ObjectWithComplexIdResponse
{
    [EncodedId]
    public string Id { get; set; }
}

public class ComplexIdTestService : Service
{
    public ObjectWithComplexIdResponse Get(GetObjectWithComplexIdRequest request)
    {
        Console.WriteLine("The requested id is {0}", request.Id);
        return new ObjectWithComplexIdResponse { Id = request.Id };
    }
}

输出:

当我们导航到localhost:8081/Object/SGVsbG8vV29ybGQsVGVzdA==时,我们在控制台中看到,根据我们的请求DTO访问Id属性会产生原始解码的Id值Hello/World,Test。值得注意的是,我们添加到响应中的普通Id会在回复中自动编码。

Result

我们回来了:

return new ObjectWithComplexIdResponse { Id = "Another/Complex/Id:Test" }

然后对客户的回复就是

{ "Id": "QW5vdGhlci9Db21wbGV4L0lkOlRlc3Q=" }

编辑 - 添加了对ID数组的支持:

使用下面的链接代码允许发送或接收已编码的ID集合。

Full Source Code - With support for array of Ids

例如:http://localhost:8081/Object/SGVsbG8vU29tZXRoaW5nL0Nvb2w=,VGhpcy9Jcy9BLVRlc3Q6SWQ=

[UsesEncodedAttribute]
[Route("/Object/{Ids}","GET")]
public class GetObjectWithComplexIdRequest : IReturn<ObjectWithComplexIdResponse>
{
    [EncodedId]
    public string[] Ids { get; set; }
}

[UsesEncodedAttribute]
public class ObjectWithComplexIdResponse
{
    [EncodedId]
    public string[] Ids { get; set; }
}

Inspector