servicestack 在 uri 中传递正斜杠

Dra*_*mmy 0 uri escaping servicestack

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

因此,当使用此路由“/api/objects/{objectCode}”从 Web api 请求资源时,如果所需的 objectCode 为 1/1,则路由应为“/api/objects/1/1”,但这会生成一个页面未发现异常。当我在以下路由“/api/objects/{objectCode}/subObjects”上请求资源时该怎么办

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

  2. 另外,理想情况下,我希望能够传递这些 objectCode 的数组,但我不能保证,objectCode 值中不会有 a ,因此分隔字符会出现在资源代码中,从而出现在 ServiceStack 的分隔符解析中,不是吗?

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

Sco*_*ott 5

不幸的是,在 URL 中使用正斜杠、逗号和其他不常见的字符会产生不需要的结果,并且显然在路由中使用起来并不容易。但可以通过对 Id 值进行编码来解决此问题。

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

透明编码/解码复合ID值

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

  • 请求过滤器将负责解码任何编码的 Id 值。
  • 响应过滤器将负责对纯 Id 值进行编码。

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

完整源代码在这里

在我的示例中,我使用了 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; } }
}
Run Code Online (Sandbox Code Playgroud)

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

public class EncodedIdAttribute : Attribute { }
Run Code Online (Sandbox Code Playgroud)

用法:

[UsesEncodedAttribute]只需向具有编码 Id 值的请求和响应 DTO添加属性即可。然后用attribute标记需要编码/解码的属性[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 };
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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

结果

如果我们回来的话:

return new ObjectWithComplexIdResponse { Id = "Another/Complex/Id:Test" }
Run Code Online (Sandbox Code Playgroud)

那么对客户端的响应将是

{ "Id": "QW5vdGhlci9Db21wbGV4L0lkOlRlc3Q=" }
Run Code Online (Sandbox Code Playgroud)

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

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

完整源代码 - 支持 Id 数组

例如: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; }
}
Run Code Online (Sandbox Code Playgroud)

督察