如何在不中断旧客户端调用的情况下向JSON WebService添加更多参数?

时间:2015-12-31 09:48:54

标签: c# asp.net json web-services soap

我想在不破坏旧客户端的电话的情况下,为我的 JSON WebService 添加更多参数。 例如:

Service.asmx

中的我的WebService
[WebMethod]
public string Ping(string msg, string additionalInfo)
{
    if (string.IsNullOrEmpty(additionalInfo))
    {
        return "Process msg with old version";
    }
    return "Process msg with new version"; ;
}

//My old web service does not have additionalInfo arguments
//public string Ping(string msg) {..}

Web.config 告诉我的WebService是基于JSON的

<system.web.extensions>
  <scripting>
       <webServices>
           <jsonSerialization maxJsonLength="50000000" />
       </webServices>
   </scripting>

如果客户使用所有参数调用我的新Json WebService =&gt;一切都很好

CallWs("http://localhost:48918/Service.asmx/Ping", '{"msg":"hello", "additionalInfo":""}')

但是所有当前的客户都不会提供additionalInfo

CallWs("http://localhost:48918/Service.asmx/Ping", '{"msg":"hello"}')

我的新WebService会立即返回错误:

string(654) "{"Message":"Invalid web service call, missing value for parameter: \u0027additionalInfo\u0027.","StackTrace":"   at System.Web.Script.Services.WebServiceMethodData.CallMethod(Object target, IDictionary`2 parameters)\r\n   at System.Web.Script.Services.WebServiceMethodData.CallMethodFromRawParams(Object target, IDictionary`2 parameters)\r\n   at System.Web.Script.Services.RestHandler.InvokeMethod(HttpContext context, WebServiceMethodData methodData, IDictionary`2 rawParams)\r\n   at System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.InvalidOperationException"}"

所以我的客户必须更改代码才能使用我的新WebService,我不希望这样。我想为我的新WebService提供默认值,最好的方法是什么?

可能重复:Can I have an optional parameter for an ASP.NET SOAP web service 但是没有一个回复对我有用。

FYI 我的客户经常通过PHP调用我的JSON WebService,他们只是向服务端点发出POST请求:

$ch = curl_init("http://localhost:48918/Service.asmx/Ping");
$wsrq = array(
    "msg" => "Hello",
    //"additionalInfo" => "World",
);
curl_setopt_array($ch, array(
    CURLOPT_POST => TRUE,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_SSL_VERIFYPEER => FALSE,
    CURLOPT_POSTFIELDS => json_encode($wsrq),
    CURLOPT_HTTPHEADER => array("Content-type: application/json; charset=utf-8"),
));
$response = curl_exec($ch);

1 个答案:

答案 0 :(得分:1)

实现这一点的正确方法似乎是为您的服务方法使用方法重载。同样对于未来的方法,我建议你使用模型:

public class MyModel
{
    public string Message { get; set; }
    public string AdditionalInfo { get; set; }
}

然后:

[WebMethod]
public string Ping(MyModel model)
{
    ...
}

这将为您提供更大的灵活性,因为您将来可以轻松添加属性而不会中断。

这就是说,有一个方法或你可以考虑的解决方法:手动反序列化(我完全不推荐它,但值得一提)。

制作没有任何参数的WebMethod:

[WebMethod]
public string Ping()

然后通过访问输入流手动读取请求正文:

[WebMethod]
public string Ping()
{
    Context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    using (var inputStream = Context.Request.InputStream)
    using (var reader = new StreamReader(inputStream))
    {
        string body = reader.ReadToEnd();

        // TODO: Worth checking the request headers before attempting JSON deserialization
        // For example the Content-Type header
        var model = JsonConvert.DeserializeObject<MyModel>(body);
        if (string.IsNullOrEmpty(model.AdditionalInfo))
        {
            return "Process msg with old version";
        }
        return "Process msg with new version"; ;
    }
}

为避免在服务方法中混合多个职责,您可以将正文流的解析移动到一些单独的扩展方法中:

public static class RequestExtensions
{
    public static T ParseRequest<T>(this HttpRequest request)
    {
        request.InputStream.Seek(0, SeekOrigin.Begin);
        using (var inputStream = request.InputStream)
        using (var reader = new StreamReader(inputStream))
        {
            string body = reader.ReadToEnd();
            return JsonConvert.DeserializeObject<T>(body);
        }
    }
}

然后是你的WebMethod:

[WebMethod]
public string Ping()
{
    var model = Context.Request.ParseRequest<MyModel>();
    if (string.IsNullOrEmpty(model.AdditionalInfo))
    {
        return "Process msg with old version";
    }
    return "Process msg with new version"; ;
}

现在客户端可以像这样调用Ping方法:

POST /WebService1.asmx/Ping HTTP/1.1
Content-type: application/json; charset=utf-8
Host: localhost:14529
Content-Length: 61

{
    "msg": "Hello",
    "additionalInfo": "add info"
}

或旧方式:

POST /WebService1.asmx/Ping HTTP/1.1
Content-type: application/json; charset=utf-8
Host: localhost:14529
Content-Length: 26

{
    "msg": "Hello"
}