I would like other web application (in .net or any other) to call my JAX-RS web service to set and open my JSF page with some passed values.
E.g.
Another web application just pass the userId and a token to open an entry page.
User will complete the entry and service will return the uniqueId for created entry.
I am confused in how I can set JSF context parameters and open that JSF page using JAX-RS. Can anyone give idea about how to set value of JSF session scoped managed bean using web service?
First of all, this question indicates a misunderstanding of purpose of "REST web services" in general. The question concretely asks to perform two rather unusual tasks with a REST web service:
Manipulating the HTTP session associated with the request.
Redirecting to a HTML page managed by a stateful MVC framework as response.
Both squarely contradict the stateless nature of REST. Those tasks aren't supposed to be performed by a REST web service. Moreover, REST web services are primarily intented to be used by programmatic clients (e.g. JavaScript or Java code), not by webbrowsers which consume HTML pages. You normally don't enter the URL of a REST webservice in browser's address bar in order to see a HTML page. You normally enter the URL of a HTML page in browser's address bar. That HTML page can in turn be produced by a HTML form based MVC framework such as JSF.
In your specific case, after the programmatic client has retrieved the unique
ID from the REST web service, then the programmatic client should in turn all by itself fire a new request to the JSF web application. E.g. as follows in Java based client (below example assumes it's a plain servlet, but it can be anything else as you said yourself):
String uniqueId = restClient.getUniqueId(userId, token);
String url = "http://example.com/context/login.xhtml?uniqueId=" + URLEncoder.encode(uniqueId, "UTF-8");
response.sendRedirect(url);
In the target JSF web application, just use <f:viewParam>/<f:viewAction> the usual way in order to grab the unique ID and perform business actions based on that. E.g. as below in login.xhtml:
<f:metadata>
<f:viewParam name="uniqueId" value="#{authenticator.uniqueId}" />
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
#ManagedBean
#RequestScoped
public class Authenticator {
private String uniqueId;
#EJB
private UserService service;
public String check() {
FacesContext context = FacesContext.getCurrentInstance();
User user = service.authenticate(uniqueId);
if (user != null) {
context.getExternalContext().getSessionMap().put("user", user);
return "/somehome.xhtml?faces-redirect=true";
}
else {
context.addMessage(null, new FacesMessage("Invalid token"));
return null; // Or some error page.
}
}
// Getter/setter.
}
Perhaps the REST webservice could for convenience even return the full URL including the unique ID so that the client doesn't need to worry about the target URL.
String uniqueIdURL = restClient.getUniqueIdURL(userId, token);
response.sendRedirect(uniqueIdURL);
On the other hand, there's a reasonable chance that you just misunderstood the functional requirement and you can just directly process the user ID and token in the JSF web application, the more likely if it runs at the same server as the REST web service and also uses HTTPS. Just add an extra <f:viewParam> and do the same business service logic as the JAX-RS resource in the <f:viewAction> method.
<f:metadata>
<f:viewParam name="userId" value="#{authenticator.userId}" />
<f:viewParam name="token" value="#{authenticator.token}" />
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to implement JAX-RS RESTful service in JSF framework
Try this in your code,
return new Viewable("/index", "FOO");
Or,
RequestDispatcher rd = request.getRequestDispatcher("{url}");
rd.forward(request, response);
Or,
return Response.status(myCode).entity(new Viewable("/index", "FOO")).build();
try these example working in using jersey,
#GET
public Response get() {
URI uri=new URI("http://nohost/context");
Viewable viewable=new Viewable("/index", "FOO");
return Response.ok(viewable).build();
}
to return something different use this approach:
#GET
public Response get() {
int statusCode=204;
Viewable myViewable=new Viewable("/index","FOO");
return Response.status(statusCode).entity(myViewable).build();
}
Hope that helpe
Related
In a standalone (selfhosted) application, I would like to have an httpserver that on a single base adress can either serve simple web pages (without any serverside dynamics/scripting, it just returns the content request files) or serve RESTful webservices:
when http://localhost:8070/{filePath} is requested, it should return the content of the file (html, javascript, css, images), just like a normal simple webserver
everything behind http://localhost:8070/api/ should just act as a normal RRESTful Web API
My current approach uses ASP.NET Web API to server both the html pages and the REST APIs:
var config = new HttpSelfHostConfiguration("http://localhost:8070/");
config.Formatters.Add(new WebFormatter());
config.Routes.MapHttpRoute(
name: "Default Web",
routeTemplate: "{fileName}",
defaults: new { controller = "web", fileName = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Default API",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
The WebController is the controller that serves the web pages with this naive code:
public class WebController : ApiController
{
public HttpResponseMessage Get(string fileName = null)
{
/// ...
var filePath = Path.Combine(wwwRoot, fileName);
if (File.Exists(filePath))
{
if (HasCssExtension(filePath))
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"text/css");
}
if (HasJavaScriptExtension(filePath))
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"application/javascript");
}
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"text/html");
}
return this.Request.CreateResponse(
HttpStatusCode.NotFound,
this.GetFileContnet(Path.Combine(wwwRoot, "404.html")),
"text/html");
}
}
And again, for everything behind /api, controllers for normal REST APIs are used.
My question now is: Am I on the right track? I kind of feel that I am rebuilding a webserver here, reinventing the wheel. And I guess that there are probably a lot of http request web browsers could make that I do not handle correctly here.
But what other option do I have if I want to self host and at the same time server REST web APIs and web pages over the same base address?
Looks like you are trying to recreate asp.net FileHandler for self host. There is a better solution though. Using Katana(an OWIN host) as the hosting layer for web API. OWIN supports hosting multiple OWIN frameworks in the same app. In your case, you can host both web API and a file handler in the same OWIN app.
Filip has a good blog post on this to get you started here. You can use configuration code like this,
public void Configuration(IAppBuilder appBuilder)
{
// configure your web api.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default-api", "api/{controller}");
appBuilder.UseWebApi(config);
// configure your static file handler.
appBuilder.UseStaticFiles();
}
IMO there is nothing wrong with what you are doing. I use self-host for delivering files, html docs as well as being a regular API. At the core, self-host is using HTTP.SYS just as IIS is.
As RaghuRam mentioned there are Owin hosts that have some optimizations for serving static files, but WCF selfhost is quite capable of getting decent perf for serving files.
See this link which uses a more straigftforward approach
Setting app a separate Web API project and ASP.NET app
RouteTable.Routes.IgnoreRoute(".js");
RouteTable.Routes.IgnoreRoute(".html");
I am trying to set some session information in one web method and return it in another web method, but the session data is always empty on the second web method. Here is what I tried
Web Method 1 - Sets session information
Method StartSession() As %String [WebMethod]
{
set %session.NewSession = 1
set %session.Data("key") = "dude"
Quit "Session Started"
}
Web Method 2 - Gets session information should return dude, but is returning blank
Method TestSession() As %String [WebMethod]
{
Quit $Get(%session.Data("key"))
}
To use sessions with Cache web services you need to set the SOAPSESSION class parameter of your web service class equal to 1.
Doing so will cause the web service to return a SOAP session header in the response. If you are using a client that was built to expect this header you may not need to set up anything else. Otherwise, your client application will have to read this header and include it in all further requests, so the server can know which session you are using. An example of this header given in the documentation is:
<csp:CSPCHD xmlns:csp="http://www.intersystems.com/SOAPheaders">value of
CPSCHD token</csp:CSPCHD>
Note that security is a separate issue that your example doesn't address.
Also note that Intersystems has decided that web services will continue to use a license for some period of time after the call has been made. I can't find documentation on this, and I believe it's something like a few seconds per call. I believe that this can cause license issues that would not occur if you used other software to provide web services, and had that other software call Cache via some mechanism other than web services. I believe this is true even when that other software carefully follows all the rules in the license agreement about named and anonymous users. However, I'm not certain about any of this licensing stuff. Still, you might want to do some testing before you commit to an architecture.
As an alternative to psr's answer another way to handle state is to use custom SOAP headers.
For example:
Create a class for your custom SOAP headers like below:
Class Headers.TimeStamp Extends %SOAP.Header
{
Property TimeSent As %TimeStamp;
}
In the web method do this:
set h=##class(Headers.TimeStamp).%New()
set h.TimeSent=$ZTIMESTAMP
do ..HeadersOut.SetAt(h,"Timestamp")
This will generate the following SOAP header:
<SOAP-ENV:Header>
<TimeStamp xmlns:hdr="http://www.myapp.org">
<TimeSent>60712,70996.027Z</TimeSent>
</TimeStamp>
</SOAP-ENV:Header>
This will allow state to be maintained within the SOAP headers rather than using Cache's session management.
I have an app written using MonoTouch that relies on a Web Services URL backend. I need the ability to set the URL of this backend dynamically at run time from within the app (or from within it's settings).
I've read this article on CodeProject that describes setting URL dynamically:
http://www.codeproject.com/Articles/12317/How-to-make-your-Web-Reference-proxy-URL-dynamic#_rating
But I can't find this option in MonoDevelop.
I've tried altering the Url property of my service, but it appears there is more to it than that. (Specifically the "references.cs" file added by the web service seems to also have the URL hard coded in various attributes).
Any help much appreciated.
Thanks!
--scotru
When Mono generates C# wrapper for SOAP web-service to you, it creates 2 constructors of wrapper. Second one contains URL parameter, which you can use to set proper URL and so switch between web-services.
Example from project, which is in production (file Reference.cs):
public partial class ServicesInfoImplService : System.Web.Services.Protocols.SoapHttpClientProtocol
...
public ServicesInfoImplService() {
this.Url = "<DEFAULT_URL>";
}
public ServicesInfoImplService(string url) {
this.Url = url;
}
...
Summary
How to create an HTTPContext within a webservice? or POST to a Handler.ashx from a webservice?
Background
I have a Cold Fusion web application that uses Forms authentication but somehow achieves Windows authentication with this script:
<cfscript>
ws = CreateObject("webservice", "#qTrim.webServiceName#");
ws.setUsername("#qTrim.trimAcct#");
ws.setPassword("#qTrim.trimpwd#");
wsString=ws.UploadFileCF("#qTrim.webserviceurl#","#objBinaryData#", "#qFiles.Filename#", "Document", "#MetaData#");
</cfscript>
Apparently, the setUsername/setPassword values map to a single Windows domain account and this works in production. (The webservice is written in C# and built with .Net 4.0. and it must be used by this domain account)
I developed a DownloadHandler.ashx which works when POSTed to by a process which is running under this domain account (I have a .Net web client with a button that defines PostBackUrl="~/DownloadHandler.ashx"). This HTTPHandler grabs a few items from the HTTPContext and then calls the above webservice method DownloadFile without problems.
My Problem
Now this ColdFusion app needs to download a file using this webservice. When the CF code POSTs an HTML form to the DownloadHandler.ashx it works - BUT ONLY IF the CF tester is using this Windows domain account. This won't work in production because the CF app supports remote anonymous users through forms authentication.
Question
Not knowing ColdFusion myself, I was thinking of the following changes:
Replicate the above CF technique such that user/pswd can be set the same and have CF invoke the ws.DownloadFile method directly
I think this would require using most of my current HTTPHandler code in my webservice but I cannot think of how to handle the output. When this handler is POSTed to, it prompts for OPEN or Save and works nicely but I'm confused on how I would stream this back from the webservice itself.
The current DownloadFile webmethod communicates with a database product and returns output to this (the current) handler:
Code
namespace WebClient
{
public class DownloadHandler : IHttpHandler
{
ASMXproxy.FileService brokerService;
public void ProcessRequest(HttpContext context)
{
brokerService = new ASMXproxy.FileService();
string recNumber = context.Request.Form["txtRecordNumber"];
brokerService.Url = context.Request.Form["txtURL"];
string trimURL = context.Request.Form["txtFakeURLParm"]; // not a real URL but parms to connect to TRIM
brokerService.Timeout = 9999999;
brokerService.Credentials = System.Net.CredentialCache.DefaultCredentials;
byte[] docContent;
string fileType;
string fileName;
string msgInfo = brokerService.DownloadFile(trimURL, recNumber, out docContent, out fileType, out fileName);
string ContentType = MIMEType.MimeType(fileType);
context.Response.AppendHeader("Content-Length", docContent.Length.ToString());
context.Response.AppendHeader("content-disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = ContentType;
context.Response.OutputStream.Write(docContent, 0, docContent.Length);
context.Response.OutputStream.Flush();
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Assuming your CF site is running on IIS and not Apache or some other web server, this might work:
Put your .cfm file that calls the webservice into its own subfolder on your site. Set the Authentication properties of that folder to use Anonymous Authentication, but set the user identity to the Windows Domain account that successfully calls the webservice (click the Set... button on the dialog shown below and enter the appropriate credentials).
I have developed a custom SharePoint web service, and deployed it to /_vti_bin/myservice.asmx. As a "regular" user, browsing to that ASMX URL works fine. When I try to browse to "/_vti_bin/myservice.asmx/js" as required to call this service from ASP.Net AJAX, I get a 403. If I browse to it as no less than a farm admin (site collection admin doesn't work), I get a 403. It is entirely possible that the farm admin's role as a local server admin is also allowing it to work.
This is my web service class:
[WebService(Namespace = "http://sharepointservices.genericnamespace.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class ApprovalSvc : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlDocument GetInboxItems(string inboxName, string s_Id)
{
// code removed
}
}
This is the art of my web part code where I am hooking up the ASP.Net AJAX stuff:
ScriptManager scriptMgr = new ScriptManager();
string webUrl = SPContext.Current.Web.Url;
ServiceReference srvRef = new ServiceReference(webUrl + "/_vti_bin/ApprovalSvc.asmx");
scriptMgr.Services.Add(srvRef);
this.Controls.Add(scriptMgr);
If I'm logged in as a farm/server admin, it works. Otherwise, no. The web service assembly is in the GAC & listed in SafeControls. Any ideas?
Good old Process Monitor to the rescue.
The facts:
The service code DLL is in the web application's bin directory, as it cannot be signed b/c it references unsigned DLLs.
The request for the service DLL is coming from ASP.Net & not SharePoint, specifically an HttpModule in the System.Web.Extensions assembly.
The solution:
Because the request didn't come through SharePoint, and identity impersonation is also turned on by default, the default NTLM permissions on the web app's BIN directory were not good enough - the user's account had no access to the BIN directory or the DLLs within it.
We gave the NT AUTHORITY\Authenticated Users Read access (not Read & Execute, not List Folder Contents, just Read) to the folder, and all is well.