I'm responsible for maintaining a web service project in c#. I have one service class with a bunch of methods that look a lot like this:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "v1/Login", ResponseFormat = WebMessageFormat.Json)]
public string Login()
{
try
{
JObject incomingRequestJson = retrieveJson();
JObject returningJson = new JObject();
string accountId = incomingRequestJson.SelectToken("accountId", true).ToString();
string username = incomingRequestJson.SelectToken("userName", true).ToString();
string password = incomingRequestJson.SelectToken("password", true).ToString();
LoginResult result = readerBLL.Login(accountId, username, password);
returningJson.Add(new JProperty("userSessionId", result.userSessionId));
returningJson.Add(new JProperty("friendlyUserName", result.friendlyUserName));
return returningJson.ToString(Newtonsoft.Json.Formatting.None);
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message.ToString(), HttpStatusCode.OK);
}
}
Note that retrieveJson()
grabs a JSON string from the HttpContext
and runs JObject.Parse()
on it.
Note also that the consumer of this service is a Flex app, so I do need the WebFaultException business with the "OK" response code (The networking component in Flex treats connection timeouts and all HTTP error codes exactly the same - no further data other than
I don't like the way I have the try-catch
block repeated in every method, so I tried making the following method:
private object invokeBusinessLogic(Func<JObject, object> f)
{
try
{
return f(retrieveJson());
}
catch (Exception ex)
{
WebFaultException<string> wfe = new WebFaultException<string>(ex.Message.ToString(), HttpStatusCode.OK);
throw wfe;
}
}
Then my service methods can look like this:
public object Login()
{
LoginResult result = (LoginResult) invokeBusinessLogic(readerBLL.Login);
returningJson.Add(new JProperty("userSessionId", result.userSessionId));
returningJson.Add(new JProperty("friendlyUserName", result.friendlyUserName));
return returningJson.ToString(Newtonsoft.Json.Formatting.None);
}
However, this has the problem that the use of JSON to carry the request data gets exposed to the business layer. Is there a better way to avoid the duplicate try-catch
blocks than this invokeBusinessLogic
idea?
1 Answer 1
I'm not sure why your change all of a sudden would expose data to the business layer when it didn't before.
If you have a pattern of methods which look like this:
public string MyServiceCall()
{
try
{
JObject incomingRequestJson = retrieveJson();
JObject returningJson = new JObject();
// do stuff
return returningJson.ToString(Newtonsoft.Json.Formatting.None);
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message.ToString(), HttpStatusCode.OK);
}
}
Then you can simply add a helper method to your service implementation:
private string ProcessServiceCall(Func<JObject, JObject> processor)
{
try
{
JObject incomingRequestJson = retrieveJson();
JObject returningJson = processor(incomingRequestJson);
return returningJson.ToString(Newtonsoft.Json.Formatting.None);
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message.ToString(), HttpStatusCode.OK);
}
}
and your service call then becomes:
public string MyServiceCall()
{
ProcessServiceCall(incomingRequestJson => {
string accountId = incomingRequestJson.SelectToken("accountId", true).ToString();
string username = incomingRequestJson.SelectToken("userName", true).ToString();
string password = incomingRequestJson.SelectToken("password", true).ToString();
JObject result = new JObject();
LoginResult result = readerBLL.Login(accountId, username, password);
result.Add(new JProperty("userSessionId", result.userSessionId));
result.Add(new JProperty("friendlyUserName", result.friendlyUserName));
return result;
}
}
This should be a simple refactoring on your service class and not expose any more json to the business logic than before.
-
\$\begingroup\$ Thanks, this is perfect. I hadn't thought of doing an anonymous function to pass to ProcessServiceCall. \$\endgroup\$Brian– Brian2014年01月06日 21:24:07 +00:00Commented Jan 6, 2014 at 21:24
-
\$\begingroup\$ Maybe reuse source code (common logic, implementation) for WebAPI, WCF Service (JSON), WCF Service (SOAP-XML)? IMHO, better samples for minimize learning curve are real applications with full source code and good patterns \$\endgroup\$Kiquenet– Kiquenet2014年09月25日 08:28:50 +00:00Commented Sep 25, 2014 at 8:28
public LoginResult Login()
in the service layer -- and that would break my clients. The current code actually sends an escaped string rather than a real JSON string :/ \$\endgroup\$