我正在使用servicestack构建一个web api,通过Web提供一些旧数据。不幸的是,数据模式不适合ServiceStack.Ormlite的标准使用。例如,没有主键,并且在一个表上(非唯一)键实际上可以包含任何字符。在我的示例数据库中,键中有“/”字符。
因此,当使用此路由“/ api / objects / {objectCode}”从web api请求资源时,如果所需的objectCode为1/1,则路由应为“/ api / objects / 1/1”,但此结果在页面中未找到异常。当我在以下路由上请求资源时,“/ api / objects / {objectCode} / subObjects”
有谁知道我应该如何解决这个问题?我应该围绕这个问题进行设计还是我可以做些什么来允许它?
另外,理想情况下我希望能够传递这些objectCodes的数组,但我不能保证objectCode值中不会有,
,所以分隔字符会出现在资源代码和ServiceStack的分隔符解析,没有?
我已经搜索过线索,但只发现有人询问查询字符串中的编码而不是URI本身。
答案 0 :(得分:4)
不幸的是,在URL中使用正斜杠,逗号和其他不常见的字符会产生不希望的结果,显然在您的路线中不易使用。但是可以通过编码Id值来解决这个问题。
我们可以使用ServiceStack极大的过滤灵活性,使值的编码和解码对您现有的ServiceStack服务完全透明。
此方法将使用“请求和响应”过滤器属性:
因此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; } }
}
标记需要编码/解码的属性的简单属性:
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会在回复中自动编码。
我们回来了:
return new ObjectWithComplexIdResponse { Id = "Another/Complex/Id:Test" }
然后对客户的回复就是
{ "Id": "QW5vdGhlci9Db21wbGV4L0lkOlRlc3Q=" }
使用下面的链接代码允许发送或接收已编码的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; }
}