Can (Should) I call a WebService from a Spring MVC app? - spring-mvc

I don't know much details about WebService. But I often assume (due to the authentication layer and response format) the code below is a also web service in Spring MVC
#RequestMapping(value = "/rest/member/account",produces={"application/json"}, method=RequestMethod.GET)
public #ResponseBody String getAccountInfo() {
JsonObject account = new JsonObject();
// this queries local DB
account = restAccountService.accountJSON();
return account.toString();
}
Now I need to modify the above code with extra WebService from different application. This extra WebService does not exist yet, I am thinking of creating it with Spring MVC just like the earlier function.
Maybe something like below
...
// this is the so called WebService to different application
SomeWebService webService = new SomeWebService();
boolean valid = webService.checkIfUserValid(username,password);
....
//this queries local DB
if (valid)
account = restAccountService.accountJSON();
...
My questions are
Is the above a common way to do this ?
How do I handled the authentication to access the other WebService ? Because I need to ensure that the call must come from the originating app.

Related

Why is accessing session state and HttpContext in WebAPI considered bad design?

I have several .asmx web services that I want to upgrade to WebAPI. These web services look somewhat like this:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class TheWebService : System.Web.Services.WebService {
[WebMethod(EnableSession = true)]
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = HttpContext.Current.Session["UserSession"] as MySessionModel;
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
Basically, I have a single-page application. I'm using Forms Auth to login and redirect users to their "profile" and then, from this page, the app uses web services to communicate with the server. The web services only return raw strings so I don't need serialization at teh web service level. For the moment, the app is hosted in IIS and soon I'll be deploying it into azure.
I've looked around on the web, and several posts suggest that using session state and HttpContext is bad design. Why is using HttpCurrent and session state a bad idea in this case?
There is nothing innately wrong with using ASP.NET Session, as long as you don't use it as a catch-all basket for any old data. Shopping carts, for example, do not belong in Session: they belong in a Shopping Cart persistence component.
Also, and I suspect the reason for the Azure tag on this question, if you are running in a load-balanced environment such as an Azure Cloud Service, you need to use an external session provider such as a SQL Database or a shared cache. Using the in-process session provider (the default) will cause very odd, often unreproducable bugs as users are switched between different servers with different copies of the session.
As for HttpContext.Current, well, for Web API, things like Inversion of Control, Dependency Injection, and simple testability are important. A clean, testable Web API version of that service might look something like this:
public class TheWebService : ApiController {
private readonly IUserSession _userSession;
public TheWebService(IUserSession userSession)
{
_userSession = userSession;
}
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = _userSession.Get();
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
public interface IUserSession
{
MySessionModel Get();
}
You could still use HttpContext.Current.Session["UserSession"] in a class like this:
public class CurrentContextUserSession : IUserSession
{
public MySessionModel Get()
{
return HttpContext.Current.Session["UserSession"] as MySessionModel;
}
}
You would then use an IoC container such as Unity or Ninject to set CurrentContextUserSession as the implementation of IUserSession for Web API to use when constructing instances of TheWebService. But when you were writing your tests, you could use a mock or stub implementation of IUserSession that had no dependency on HttpContext.Current.
In your specific example, you are using the Session only inside the WebMethod, which is fine as it is already coupled to ASP.NET but many people tend to use this at other layers of their application which is a really bad design.
Common problems of using HttpContext.Current in those layers are:
the code cannot be easily unit tested in isolation
the code is tightly coupled to an ASP.NET context
This being said, having stateful services that depend on the session is bad design. In the example you have shown, that's an ASMX WebService which is depending on the ASP.NET Session state meaning that the client should be passing cookies around in order to invoke this service. Also stateful services are harder to scale.

UI to Web Services communication with custom authorization using .NET technologies

I'm trying to design a web application that would user WCF services to access data and provide business logic. So in general the whole system would look like that:
UI (ASP.NET MVC)
BusinessLayer (WCF Services)
DataLayer (Entity Framework)
Date (SQL Server Database)
All parts of the system will resist on the same, closed environment, so I'm going to use Certificates to secure ASP.NET <-> WCF connection. Database connection would use standard EF securities, Connection String and Windows Authentication.
The application has to provide authentication and authorization functionality. I'm going to move most of that into ASP.NET, so there will be ValidateUserAuth() service method, which will be used to validate credentials, but the result (with UserRole that user belongs to) will be then used by ASP to create user session.
After that, every Service Method call needs to know the UserRole of current user, to return proper results (or say 'Access denied' if it's necessary). Problem is I don't want to pass that UserRole as a parameter for every Service Method! I'd like to make it happen automatically. Is it even possible with WCF?
All I need is:
Every service call made from ASP.NET app will be extended with User data taken from current ASP Session.
Service Method invoked by that call will be able to recieve that User data and use it to provide results according to user permissions.
All this would happen somekind on background, so there will be no additional UserDetails method parameter added to every Service Method exposed from Service.
I read a lot about WCF itself, but found anything that could met my requirements. I hope I just missed it and it's still possible.
Passing user roles from the client to the server in a plain form would be a design mistake. Clients could easily misuse your services by calling them freely, outisde of the application scope.
Why don't you rely on a role provider? This way all you pass from the client is the identity (could even be a forms cookie) and then at the server side you read all roles. You could probably even use a built in mechanism to cache roles in a role cookie.
Some time ago I wrote two tutorials on wcf securing with forms authentication so that integration between web pages and active clients is easy
http://netpl.blogspot.com/2008/02/clickonce-webservice-and-shared-forms.html
http://netpl.blogspot.com/2010/04/aspnet-forms-authentication-sharing-for.html
I decided to use MessageInspector for that:
On Client-side:
Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest
Dim requestMessageProperty = New HttpRequestMessageProperty()
Dim currentUser = Authentication.AuthenticatedStaffcareUser
If currentUser Is Nothing Then Throw New ApplicationException()
requestMessageProperty.Headers("UserName") = currentUser.UserName
requestMessageProperty.Headers("UserId") = currentUser.UserID
requestMessageProperty.Headers("UserRole") = currentUser.UserRole
requestMessageProperty.Headers("EffectiveDate") = currentUser.EffectiveDate
request.Properties(HttpRequestMessageProperty.Name) = requestMessageProperty
Return Nothing
End Function
And Server-side:
Public Function AfterReceiveRequest(ByRef request As Message, channel As IClientChannel, instanceContext As InstanceContext) As Object Implements IDispatchMessageInspector.AfterReceiveRequest
Dim messageProperty = CType(OperationContext.Current.IncomingMessageProperties(HttpRequestMessageProperty.Name), HttpRequestMessageProperty)
Dim userName = messageProperty.Headers("UserName")
Dim userId = Integer.Parse(messageProperty.Headers("UserId"))
Dim userRole = messageProperty.Headers("UserRole")
Dim effectiveDate = DateTime.Parse(messageProperty.Headers("EffectiveDate"))
Dim identity = New AppServerUserIdentity(userName, userId, userRole, effectiveDate)
Dim principal = New AppServerUserPrincipal(identity)
Threading.Thread.CurrentPrincipal = principal
Return Nothing
End Function
I also had to set custom AuthorizationPolicy to prevent standard one from overwriting Thread.CurrentPrincipal:
Public Function Evaluate(evaluationContext As EvaluationContext, ByRef state As Object) As Boolean Implements IAuthorizationPolicy.Evaluate
Dim principal = TryCast(Threading.Thread.CurrentPrincipal, AppServerUserPrincipal)
If principal Is Nothing Then
Return False
Else
evaluationContext.Properties("Principal") = principal
Return True
End If
End Function

How can I get objects from ASP server to my Silverlight 3 app?

I have little Silverlight app that needs to make queries to server, is it possible to return objects to silverlight app or how can I communicate with server?
Use a WCF Service. As long as your objects are Serializable, the runtime will encode and decode them for you transparently.
A simple Silverlight-enabled WCF service looks something like this:
using System.ServiceModel;
using System.ServiceModel.Activation;
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class YourService
{
[OperationContract]
public string DoStuff(string arg)
{
return arg + "stuff";
}
}
You can replace "string" with your datatype by creating a [DataContract].
In my opinion it might be best to use web services to ship whatever is needed to your Silverlight application. I suggest that you use the WebClient class in combination with the URI class to obtain the data. Example:
Uri uri = new Uri(//the url of you webservice, UriKind.RelativeOrAbsolute);
Now create an instance of the WebClient class and add a callback to be called when the read from the web service is completed:
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(CallbackMethod);
wc.OpenReadAsync(uri);
When the data is retrieved from the server, the CallbackMethod is called. The method has an EventArgs object that contains a property called result. You can obtain your data using that property.
Silverlight doesnt need ASP at all to function, if you have a database on a seperate server check out WCF, and then get Silverlight to communicate with the WCF service and then the service with the database, if you want something more transparent, then try out WCF RIA services, this allows you to have a middle-tier approach to data access in silverlight

Loading the initial state on a silverlight application based on asp.net session

I'm writing a silverlight application that resembles a shopping cart system.
This app can only be launched from the asp.net website after the user is logged in to the site.
Upon first load, the app will send a request to the backend through WCF service to retreive all the existing items in the shopping cart.
Therefore the silverlight app needs to know who the current user is, to find which shopping cart to load.
I found there are a couple of ways so far, but not happy with any of them:
using wcf aspnet compat. silverlight can ask who the current user is by asking the wcf service.
pass parameters from the page to xaml by using xaml.InitParameters and pass in the minimum amount of information to identify a user in a serialized format.
pass parameters through query string to xaml (apparently this is also possible)
Can anyone share the best practice to achieve this?
Thanks
We use the first solution in our projects. You haven't to invent any type of serialization format or so in this case. A disadvantage of this approach - extra async logic at startup.
The example of service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class UserInfoService : IUserInfoService
{
public UserInfo GetUserInfo()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
return null;
var userInfo = new UserInfo
{
Login = HttpContext.Current.User.Identity.Name,
Fullname = ...,
};
return userInfo;
}
}
Sending userid via initParams or query string is not good idea I think. Such things should be more hidden.
The real important thing is to verify user on server on each service call because anyone can call your services in similar way as your app.
HTH

How to provide a Session/Host object for use in both a windows and web application?

I have a web application that makes heavy use of the Session state to store information about the current user, their personal settings, record their session history and so on.
I have found myself retrieving this session information in my business layer, like so:
((UserSession)HttpContext.Current.Session["UserSession"]).User.Info
This poses a problem - at some point in the future my application will have a Windows client which obviously cannot reference the web Session state. So I need a host or customized session class that I can reference in my business layer that is agnostic of whether the application is running on the web or desktop. Something like:
IHost.User.Info
Behind the scenes, the web implementation will obviously utilize the Session state to store information, but I need to hide this away from my business layer. Has anyone solved this problem or got any practival advice on how best to approach this?
Help appreciated.
Assuming that the business layer is a separate DLL, I would never add a reference to System.Web and in consequence I would never use the Session object directly. This would lead to a different design of the business layer and of the exposed interfaces to a client (either web or winforms).
That said, as a quick workaround I would suggest to write a wrapper class in your business layer that hides the Session object from your code. Your calls from code will be something like this:
((UserSession) DualContext.Current["UserSession"]).User.Info
and the wrapper implementation will be something like this (not completed and tested):
public class DualContext
{
private Dictionary<string, object> winFormsSession = new Dictionary<string, object>();
private static readonly DualContext instance = new DualContext();
public static DualContext Current
{
get { return instance; }
}
public object this[string key]
{
get
{
if (HttpContext.Current != null)
return HttpContext.Current.Session[key];
else
return winFormsSession[key];
}
set
{
if (HttpContext.Current != null)
HttpContext.Current.Session[key] = value;
else
winFormsSession[key] = value;
}
}
}
It would take some re-architecting, but if you switch from using Session State to User Profiles you could then use Client Application Services to share the information.
http://msdn.microsoft.com/en-us/library/bb384297.aspx
I guess you need to create a webservice or RESTfull service. The service will return an XML file representing your user information. You will be able to invoke the service wither from you windows or web application.

Resources