Struts2 convention and redirect parameters - struts2

I use a Struts2 Convention plug-in to map my actions. Please, help me to solve the following problem. Here I have an action mapping
#Action(value="/{categorie:\\w+}/{hoofdgroep:\\w+}/{artikelgroep:\\w+}/", results = {
#Result(name="success", location="articlelist.jsp"),
#Result(name="maingroup", location="/%{categorie}/%{hoofdgroep}/", type="redirect"),
#Result(name="category", location="/%{categorie}/", type="redirect")
}, interceptorRefs = {
...
})
public String execute() throws Exception {
...
Category category = service.getCategory(categorie);
if (category == null) return NONE;
...
MainGroup mGroup = service.getMainGroup(hoofdgroep);
if (mGroup == null) return "category";
...
ArticleGroup artGroup = service.getArticleGroup(artikelgroep);
if (artGroup == null) return "maingroup";
...
return SUCCESS;
}
When, for instance, there is no artGroup for specified artikelgroep it should redirect link _http://site/categorie/hoofdgroep/artikelgroep/ to url _http://site/categorie/hoofdgroep/ which it perfectly does. The only problem here is that it also prepends additional parameters which are undesired. So link _http://site/categorie/hoofdgroep/artikelgroep/ is redirected to _http://site/categorie/hoofdgroep/?categorie=categorie&hoofdgroep=hoofdgroep&artikelgroep=artikelgroep.
My question is How to get rid of these parameters?
Here are some config params from my struts.properties file
...
struts.serve.static=false
struts.ognl.allowStaticMethodAccess=true
struts.enable.DynamicMethodInvocation=false
struts.action.extension= ,
struts.url.includeParams=none
struts.enable.SlashesInActionNames=true
struts.mapper.alwaysSelectFullNamespace=false
struts.patternMatcher=regex
struts.convention.default.parent.package=app-default
struts.convention.action.packages=...
struts.convention.action.alwaysMapExecute=false
struts.convention.package.locators.disable=true
struts.convention.relative.result.types=dispatcher
struts.convention.result.path=/WEB-INF/jsp/
So basically is this a bug or it should work this way?
Perhaps it is not so elegant solution but here what I have done. I overrode org.apache.struts2.dispatcher.ServletRedirectResult#getProhibitedResultParams
public class ServletRedirectResult
extends org.apache.struts2.dispatcher.ServletRedirectResult
{
public ServletRedirectResult() {
super();
initProhibitedResultParams();
}
public ServletRedirectResult(String location) {
super(location);
initProhibitedResultParams();
}
public ServletRedirectResult(String location, String anchor) {
super(location, anchor);
initProhibitedResultParams();
}
private List<String> prohibitedParamNames;
private void initProhibitedResultParams() {
String[] parentParams = (String[])super.getProhibitedResultParams().toArray();
int len = parentParams.length;
String[] params = new String[len + 4];
for (int i = 0; i < len; i++) {
params[i] = parentParams[i];
}
params[len] = "statusCode";
// TODO: This is a temporary solution because RegexPatternMatcher puts parameters
// from urls into ResultConfig for some reason.
params[len+1] = "categorie";
params[len+2] = "hoofdgroep";
params[len+3] = "artikelgroep";
prohibitedParamNames = Arrays.asList(params);
}
protected List<String> getProhibitedResultParams() {
return prohibitedParamNames;
}
}

What you describe is the default behaviour of both com.opensymphony.xwork2.util.NamedVariablePatternMatcher and org.apache.struts2.util.RegexPatternMatcher however it not the behaviour of com.opensymphony.xwork2.util.WildcardHelper (which is the default implementation)
From what you have shown the default implementation can handle what you are doing with far less headaches (regular wildcard matching).
Consulting this page: http://struts.apache.org/2.3.1.2/docs/wildcard-mappings.html
It states for "Parameters in namespaces" (I know you are not using this):
From Struts 2.1+ namespace patterns can be extracted as request
parameters and bound to the action.
However this equally applies to what is happening in the action and it really seems to be the only behaviour (where I would assume from "can be" that there would be another choice when it should have really been written as "... namespace/action patterns are extracted as request parameters...") and it seems to apply to the regex pattern matching equally, it would be nice for the documentation to more explicitly state this.
From your comments I can better understand what you are doing...
Why don't you simply set up three actions for:
*/*/*, */* and *
Then just pass the numbered parameters into the action?

I was digging into the code of org.apache.struts2.dispatcher.ServletRedirectResult#doExecute. Probably this piece prepends undesired parameters
ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());
if (resultConfig != null)
{
Map<String, String> resultConfigParams = resultConfig.getParams();
for (Map.Entry<String, String> e : resultConfigParams.entrySet())
{
if (!getProhibitedResultParams().contains(e.getKey()))
{
String potentialValue = e.getValue() == null ? "" : conditionalParse(e.getValue(), invocation);
if (!suppressEmptyParameters || ((potentialValue != null) && (potentialValue.length() > 0)))
{
requestParameters.put(e.getKey(), potentialValue);
}
}
}
}
There is nothing wrong with this code. And the question is Why those three parameters appeared in the ResultConfig? Because it is working like, when you write like so
<result name="maingroup" type="redirect">
<param name="location">/${categorie}/${hoofdgroep}/</param>
<param name="namespace">/</param>
<param name="categorie">${categorie}</param>
<param name="hoofdgroep">${hoofdgroep}</param>
<param name="artikelgroep">${artikelgroep}</param>
</result>

Related

Middleware to Encrypt Query string

I am writing a .Net Core Middleware to encrypt the query string parameters, where I want to user to see something like
?enc=VXzal017xHwKKPolDWQJoLACDqQ0fE//wGkgvRTdG/GgXIBDd1
while the code sees this
?user=123&account=456.
I encrypt the params using a IDataProtector. The Invoke() in my middleware looks like the below code
if (UriHelper.GetEncodedUrl(context.Request).Contains("?"))
{
string query = ExtractQuery((context.Request.GetEncodedUrl()));
int indexOfEnc = query.IndexOf(PARAMETER_NAME, StringComparison.OrdinalIgnoreCase);
if (indexOfEnc > -1)
{
var enc = context.Request.Query[PARAMETER_NAME];
enc = Decrypt(enc);
context.Request.Path = new PathString(context.Request.Path.Value + enc);
await _next.Invoke(context);
}
else if (context.Request.Method == "GET" || context.Request.Method == "POST")
{
// Encrypt the query string and redirects to the encrypted URL.
// Remove if you don't want all query strings to be encrypted automatically.
string encryptedQuery = Encrypt(query);
string tempRawUrl = UriHelper.GetEncodedUrl(context.Request).ToLower();
if (!(context.Request.Method == "POST" && tempRawUrl.Contains("ha")))
{
context.Response.Redirect(context.Request.Path.Value + "?" + PARAMETER_NAME + "=" + encryptedQuery);
}
}
}
else
{
await _next.Invoke(context);
}
The First time when I login and enter the user/pass, the code comes in to the elseif section above and gets encrypted fine. I look for the "enc" query param the next time and while it gets decrypted and the path looks good, the
**await _next.Invoke(context);**
in the if section does nothing. I am expecting it to go to the controller to validate the user/pass.
Bear with me here please, this is my first middleware and I am trying to replace the httphandlers in my legacy code.
Any help is appreciated. I have spent almost 5 hours on this and cant seem to figure it out.
You may take a look at the IQueryFeature and the IResponseFeature. In ASP.NET Core, features allow to override behaviours of basics objects
like HttpRequest & HttpResponse object.
You could simply wrap the existing IQueryFeature for transparent decryption.
And for the query encryption, wrap the existing IResponseFeature for transparent encryption.
Set the wrappers within the middleware.
httpContext.Features.Set<IQueryFeature>(new TransparentDecryptionQueryFeature(httpContext.Features.Get<IQueryFeature>));
httpContext.Features.Set<IResponseFeature>(new TransparentEncryptionResponseFeature(httpContext.Features.Get<IResponseFeature>));
By doing so, all middlewares executing after yours will use the "Transparent Feature".
public class TransparentDecryptionQueryFeature : IQueryFeature
{
privare readonly IQueryCollection _store;
public TransparentDecryptionQueryFeature(IQueryFeature feature)
{
_store = new TranparentDecryptionQueryCollection(feature.Query);
}
public IQueryCollection Query
{
get
{
return _store;
}
set
{
_store = new TransparentDecryptionQueryCollection(value);
}
}
}
public class TransparentDecryptionQueryCollection : IQueryCollection
{
private readonly IQueryCollection _inner;
public TransparentDecryptionQueryCollection(IQueryCollection inner)
{
var store = new Dictionary<string, StringValues>()
foreach (var item in inner)
{
if (item.Key == PARAMETER_NAME)
{
// TODO : Adds all the decrypted query parameters in the store
}
else
{
store.Add(item);
}
}
_inner = new QueryCollection(store);
}
// implement other methods by delegating with _inner object
}
I changed the code to.
if (indexOfEnc > -1)
{
var enc = context.Request.Query[PARAMETER_NAME];
enc = "?" + Decrypt(enc);
Microsoft.AspNetCore.Http.QueryString queryString = new Microsoft.AspNetCore.Http.QueryString(enc);
context.Request.QueryString = queryString;
await _next.Invoke(context);
}
and it does work now. I still think I am missing something here. Is there a better way to do this ?

Include Entity navigation properties using a Service Reference

I am using a WCF Data Services class that exposes an entity framework model via the OData protocol like so:
public class Service : EntityFrameworkDataService<MyEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}
}
I consume this service through a service reference in a web solution. I am having problems including all the navigation properties for the entity. I cannot use the following syntax because I do not know what type of entity the user may be requesting:
I CANNOT USE
MyEntities.Customer.Expand("Address");
or
MyEntities.Customer.Include("Address");
What I am currently doing is building a URI string with the $expand=Entity1,Entity2 syntax and then executing that against my service as follows:
public static QueryOperationResponse<object> GetList(string entitySetName, params string[] preloads)
{
StringBuilder stringBuilder = new StringBuilder();
string queryString = string.Empty;
object result = null;
Uri dataAccessURI;
stringBuilder.Append(ServiceReferenceURI.AbsoluteUri);
stringBuilder.Append(entitySetName);
if (preloads != null)
{
for (int i = 0; i <= preloads.Length - 1; i++)
{
queryString = i == 0 ? "?$expand=" : ",";
stringBuilder.AppendFormat("{0}{1}", queryString, preloads[i]);
}
}
dataAccessURI = new Uri(stringBuilder.ToString());
try
{
result = TitanEntities.Execute<object>(dataAccessURI, "GET", true);
}
catch (Exception ex)
{
// log any errors to the console
WriteConsoleMessage(ex.Message, DataAccessEventType.Error);
}
return (QueryOperationResponse<object>)result;
resulting URI string is similar to this:
http://192.168.0.196/Service.svc/AliquotPreparation?$expand=Aliquot,AliquotPrepBatch,AnalysisPreparationMethod,Unit,Employee,Unit,PreparationMethod,State
To me this is a crappy implementation. It is all I could come up with right now though. The problem is, if there are A LOT of navigation properties the $expand command gets too long and the URI reaches it's character limit!
So how can I implement this through a service reference? I would greatly appreciate someone's help!!!

Struts2 : actionInvocation.invoke() method gives null pointer exception

In my application I have added interceptor to filter the request.Here each user is associated with a list of menu. So if user try to access page which is not associated to him then we will redirect him to unauthorizedUser.jsp page otherwise we will let the user to access the page.
Here is my interceptor code ...
#Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
String returnAction = "unauth.user";
Map<String, String> keyValMap = FCCommonUtils.getALLMenuIdMap();
ActionContext context = actionInvocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
HttpSession session = null;
if (request != null) {
session = request.getSession(false);
String contextPath = request.getContextPath();
contextPath = contextPath + RequestURIDtls.SEPERATOR;
String reqURI = request.getRequestURI().substring(contextPath.length(), request.getRequestURI().length());
String requestedRole = keyValMap.get(reqURI);
if (requestedRole != null && session != null) {
UserInfoUIForm userForm = (UserInfoUIForm) session.getAttribute(WebConstants.USER_INFO);
if (userForm != null) {
List<Long> userRoleLst = FCCommonUtils.getmenuids(userForm.getRoleId());
if (userRoleLst.contains(new Long(requestedRole))) {
//TODO : GUNJAN : NEED TO DO R&D WHY actionInvocation.invoke() CREATES NULL POINTER EXCEPTION
//returnAction=actionInvocation.invoke();
returnAction = "success";
} else {
returnAction = "unauth.user";
}
} else {
returnAction = "unauth.user";
}
} else {
returnAction = "unauth.user";
}
} else {
returnAction = "unauth.user";
}
return returnAction;
}
In above code returnAction=actionInvocation.invoke() gives null pointer exception.
Here is my struts.xml configuration to access the page ..
<action name="viewCorporate" class="com.ndil.web.corporate.MstCorporateAction" method="viewCorporatePage">
<interceptor-ref name="menuFilterInterceptor" />
<result name="unauth.user">/jsp/unAuthUser.jsp</result>
<result name="success">/jsp/mngCorporate.jsp</result>
</action>
Can any one suggest me why actionInvocation.invoke() gives null pointer exception ???
Thanks,
Gunjan Shah.
Free code review.
1) Intercept result decared as variable, unused.
2) Said value should be a constant anyway.
3) Variable is named incorrectly--it's not an action name, it's a result name.
4) If you're in an interceptor, you've gotten a request--there's just no way for this to be null. If it is null, something far more serious than an unauthorized user has occurred, and the world should blow up.
5) Similarly, unless you've specifically configured your entire app not to create sessions, checking for a session is redundant. If you don't, something has gone horribly wrong. Check for known session attributes to determine if a user is logged in, not for the presence of the session itself--much easier.
IMO both 4 and 5, if handled at all, should be handled with declarative exceptions. In that state, the web app is likely inoperable--peg the user to HTTP 500 or similar.
6) The nested conditionals are way too deep. Strict adherence to "one return per method" creates difficult-to-understand code, particularly when a method has deeply-nested conditionals.
7) It looks like you're relying on form data to determine the user's role. This is inherently insecure; user role information should be kept in the session, where it can't be easily manipulated.
8) Some miscellaneous tweaks leave us with this:
public class FooInterceptor {
private static final String UNAUTHORIZED_USER = "unauth.user";
public String intercept(ActionInvocation actionInvocation) throws Exception {
ActionContext context = actionInvocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
if (request == null) {
return UNAUTHORIZED_USER;
}
HttpSession session = request.getSession(false);
if (session == null) {
return UNAUTHORIZED_USER;
}
Long requestedRole = getRequestedRole(request);
if (requestedRole == null) {
return UNAUTHORIZED_USER;
}
UserInfoUIForm userForm = (UserInfoUIForm) session.getAttribute(WebConstants.USER_INFO);
if (userForm == null) {
return UNAUTHORIZED_USER;
}
List<Long> userRoles = FCCommonUtils.getmenuids(userForm.getRoleId());
return userRoles.contains(requestedRole) ? ActionSupport.SUCCESS : UNAUTHORIZED_USER;
}
private Long getRequestedRole(HttpServletRequest request) {
String contextPath = request.getContextPath() + RequestURIDtls.SEPARATOR;
String reqURI = request.getRequestURI().substring(contextPath.length(), request.getRequestURI().length());
try {
return Long.valueOf(FCCommonUtils.getALLMenuIdMap().get(reqURI));
} catch (NumberFormatException e) {
return null;
}
}
}
While testing the method remains relatively difficult, it's much easier to understand the precise testing needs. It's easier to read, because you no longer have to wonder "what if the opposite is true?" as in the deeply-nested code.
Use this:
<action name="viewCorporate" class="com.ndil.web.corporate.MstCorporateAction" method="viewCorporatePage">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="menuFilterInterceptor" />
<result name="unauth.user">/jsp/unAuthUser.jsp</result>
<result name="success">/jsp/mngCorporate.jsp</result>
</action>
Struts won't add this default interceptor automatically when you are explicitly specifying a interceptor for action declaration.

POST request to Struts2 with REST plugin not receiving response

I have a struts2 application which uses the struts2-rest-plugin v.2.2.3.
Everything is working great when it comes to the routing of the requests to the actions and its methods and I'm also using ModelDriven to specify the object to serialise when using extensions like JSON and XML.
The problem I'm having is that when I send a POST or PUT request to the struts layer I just get an empty response.
I am sending a POST request to the action like so: http://localhost:8080/alert-settings!update.json. I have a breakpoint in that method and it gets called and the code runs and completes. I have a feeling the issue might be that I am trying to use the ModelDriven interface to send me back the response and for some reason the rest-plugin doesn't like this but I don't know why it would behave like that.
Is there a known issue with receiving responses from POST requests while using the rest plugin? I have looked everywhere and can't find anything about it really.
Any help appreciated and I can provide any more details on request.
I encountered the same issue. Have you tried to set in the struts.xml file:
struts.rest.content.restrictToGET = false
See the last setting on the rest plugin docs
I actually figured out that it was a line in the rest plugin causing this:
// don't return any content for PUT, DELETE, and POST where there are no errors
if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
target = null;
}
This is in org.apache.struts2.rest.RestActionInvocation in the selectTarget() method. I find this to be quite annoying as it doesn't really follow the REST architecture, id like the option to be able to return response objects for POST, DELETE and PUT requests in some cases.
I worked around this by extending RestActionProxyFactory and RestActionInvocation and specifying the use of this in my struts xml like so:
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="restOverride" class="uk.co.ratedpeople.tp.rest.RPRestActionProxyFactory" />
<constant name="struts.actionProxyFactory" value="restOverride" />
This allows me to use the struts plugin throughout while returning object on POST requests.
RestActionProxyFactory
public class RPRestActionProxyFactory extends RestActionProxyFactory {
#Override
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext) {
if (namespace.startsWith(this.namespace)) {
ActionInvocation inv = new RPRestActionInvocation(extraContext, true);
container.inject(inv);
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
} else {
return super.createActionProxy(namespace, actionName, methodName, extraContext, executeResult, cleanupContext);
}
}
}
RestActionInvocation
public class RPRestActionInvocation extends RestActionInvocation {
public RPRestActionInvocation(Map extraContext, boolean pushAction) {
super(extraContext, pushAction);
}
#SuppressWarnings("unchecked")
#Override
protected void selectTarget() {
// Select target (content to return)
Throwable e = (Throwable)stack.findValue("exception");
if (e != null) {
// Exception
target = e;
hasErrors = true;
} else if (action instanceof ValidationAware && ((ValidationAware)action).hasErrors()) {
// Error messages
ValidationAware validationAwareAction = ((ValidationAware)action);
Map errors = new HashMap();
if (validationAwareAction.getActionErrors().size() > 0) {
errors.put("actionErrors", validationAwareAction.getActionErrors());
}
if (validationAwareAction.getFieldErrors().size() > 0) {
errors.put("fieldErrors", validationAwareAction.getFieldErrors());
}
target = errors;
hasErrors = true;
} else if (action instanceof ModelDriven) {
// Model
target = ((ModelDriven)action).getModel();
} else {
target = action;
}
// don't return any content for PUT, DELETE, and POST where there are no errors
// if (!hasErrors && !"get".equalsIgnoreCase(ServletActionContext.getRequest().getMethod())) {
// target = null;
// }
}
}
I've used struts actions with mixed result types in the past, returning json, xml, and tiles for instance. I'm not sure if it's the recommended way to do it but it requires some configuration using struts.xml even though conventions are being used. Maybe you've already done this, not sure there isn't enough info provided to tell.
Struts.xml settings:
<constant name="struts.convention.default.parent.package" value="restful"/>
<package name="restful" extends="rest-default, struts-default, json-default">
<result-types>
<result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" />
<result-type name="json" class="com.googlecode.jsonplugin.JSONResult"/>
</result-types>
....
</package>
I have setup the extra result types to be used on specific actions later. In the action class you can then setup your result types by action or method.
Action Class:
#Results({
#Result(name = "JsonSuccess", type = "json"),
#Result(name = "success", type = "tiles", location = "/tickets.tiles")
})
public class EventController extends RestActionSupport implements ModelDriven<EventBean>{
...
}
Something else to note about json results, I've noticed that when I have a serializable object being returned as a result, if that object contains other complex objects with a getter/setter that returns the embedded object, I will often receive an empty result or no result. I often end up writing json wrapper objects to use for my json results with getters/setters that only return java types (String, int, Boolean, etc) and not the embedded objects. I think that've solved this using delegate getters/setters but I'll have to go back and look at some old code.

How do I convert an HttpRequestBase into an HttpRequest object?

inside my ASP.NET MVC controller, I've got a method that requires an HttpRequest object. All I have access to is an HttpRequestBase object.
Is there anyway I can somehow convert this?
What can/should I do??
You should always use HttpRequestBase and HttpResponseBase in your application as opposed to the concrete versions which are impossible to test (without typemock or some other magic).
Simply use the HttpRequestWrapper class to convert as shown below.
var httpRequestBase = new HttpRequestWrapper(Context.Request);
Is it your method, so you can re-write it to take HttpRequestBase? If not, you can always get the current HttpRequest from HttpContext.Current.HttpRequest to pass on. However, I often wrap access to the HttpContext inside a class like mentioned in ASP.NET: Removing System.Web Dependencies for better unit testing support.
You can just use
System.Web.HttpContext.Current.Request
The key here is that you need the full namespace to get to the "correct" HttpContext.
I know it's been 4 years since this question was asked, but if this will help somebody, then here you go!
(Edit: I see that Kevin Hakanson already gave this answer...so hopefully my response will help those people who just read answers and not comments.) :)
To get HttpRequest in ASP.NET MVC4 .NET 4.5, you can do the following:
this.HttpContext.ApplicationInstance.Context.Request
Try to use/create a HttpRequestWrapper using your HttpRequestBase.
Typically when you need to access the HttpContext property in a controller action, there is something you can do better design wise.
For example, if you need to access the current user, give your action method a parameter of type IPrincipal, which you populate with an Attribute and mock as you wish when testing. For a small example on how, see this blog post, and specifically point 7.
There is no way to convert between these types.
We had a similar case. We rewrote our classes/web services methods so that they use HttpContextBase, HttpApplicationStateBase, HttpServerUtilityBase, HttpSessionStateBase... instead of the types of close name without the "Base" suffix (HttpContext, ... HttpSessionState). They are a lot easier to handle with home-made mocking.
I feel sorry you couldn't do it.
This is an ASP.Net MVC 3.0 AsyncController which accepts requests, converts the inbound HttpRequestBase MVC object to a System.Web.HttpWebRequest. It then sends the request asynchronously. When the response comes back, it converts the System.Web.HttpWebResponse back into an MVC HttpResponseBase object which can be returned via the MVC controller.
To answer this question explicitly, I guess you'd only be interested in the BuildWebRequest() function. However, it demonstrates how to move through the whole pipeline - converting from BaseRequest > Request and then Response > BaseResponse. I thought sharing both would be useful.
Through these classes, you can have an MVC server which acts as a web proxy.
Hope this helps!
Controller:
[HandleError]
public class MyProxy : AsyncController
{
[HttpGet]
public void RedirectAsync()
{
AsyncManager.OutstandingOperations.Increment();
var hubBroker = new RequestBroker();
hubBroker.BrokerCompleted += (sender, e) =>
{
this.AsyncManager.Parameters["brokered"] = e.Response;
this.AsyncManager.OutstandingOperations.Decrement();
};
hubBroker.BrokerAsync(this.Request, redirectTo);
}
public ActionResult RedirectCompleted(HttpWebResponse brokered)
{
RequestBroker.BuildControllerResponse(this.Response, brokered);
return new HttpStatusCodeResult(Response.StatusCode);
}
}
This is the proxy class which does the heavy lifting:
namespace MyProxy
{
/// <summary>
/// Asynchronous operation to proxy or "broker" a request via MVC
/// </summary>
internal class RequestBroker
{
/*
* HttpWebRequest is a little protective, and if we do a straight copy of header information we will get ArgumentException for a set of 'restricted'
* headers which either can't be set or need to be set on other interfaces. This is a complete list of restricted headers.
*/
private static readonly string[] RestrictedHeaders = new string[] { "Accept", "Connection", "Content-Length", "Content-Type", "Date", "Expect", "Host", "If-Modified-Since", "Range", "Referer", "Transfer-Encoding", "User-Agent", "Proxy-Connection" };
internal class BrokerEventArgs : EventArgs
{
public DateTime StartTime { get; set; }
public HttpWebResponse Response { get; set; }
}
public delegate void BrokerEventHandler(object sender, BrokerEventArgs e);
public event BrokerEventHandler BrokerCompleted;
public void BrokerAsync(HttpRequestBase requestToBroker, string redirectToUrl)
{
var httpRequest = BuildWebRequest(requestToBroker, redirectToUrl);
var brokerTask = new Task(() => this.DoBroker(httpRequest));
brokerTask.Start();
}
private void DoBroker(HttpWebRequest requestToBroker)
{
var startTime = DateTime.UtcNow;
HttpWebResponse response;
try
{
response = requestToBroker.GetResponse() as HttpWebResponse;
}
catch (WebException e)
{
Trace.TraceError("Broker Fail: " + e.ToString());
response = e.Response as HttpWebResponse;
}
var args = new BrokerEventArgs()
{
StartTime = startTime,
Response = response,
};
this.BrokerCompleted(this, args);
}
public static void BuildControllerResponse(HttpResponseBase httpResponseBase, HttpWebResponse brokeredResponse)
{
if (brokeredResponse == null)
{
PerfCounters.ErrorCounter.Increment();
throw new GriddleException("Failed to broker a response. Refer to logs for details.");
}
httpResponseBase.Charset = brokeredResponse.CharacterSet;
httpResponseBase.ContentType = brokeredResponse.ContentType;
foreach (Cookie cookie in brokeredResponse.Cookies)
{
httpResponseBase.Cookies.Add(CookieToHttpCookie(cookie));
}
foreach (var header in brokeredResponse.Headers.AllKeys
.Where(k => !k.Equals("Transfer-Encoding", StringComparison.InvariantCultureIgnoreCase)))
{
httpResponseBase.Headers.Add(header, brokeredResponse.Headers[header]);
}
httpResponseBase.StatusCode = (int)brokeredResponse.StatusCode;
httpResponseBase.StatusDescription = brokeredResponse.StatusDescription;
BridgeAndCloseStreams(brokeredResponse.GetResponseStream(), httpResponseBase.OutputStream);
}
private static HttpWebRequest BuildWebRequest(HttpRequestBase requestToBroker, string redirectToUrl)
{
var httpRequest = (HttpWebRequest)WebRequest.Create(redirectToUrl);
if (requestToBroker.Headers != null)
{
foreach (var header in requestToBroker.Headers.AllKeys)
{
if (RestrictedHeaders.Any(h => header.Equals(h, StringComparison.InvariantCultureIgnoreCase)))
{
continue;
}
httpRequest.Headers.Add(header, requestToBroker.Headers[header]);
}
}
httpRequest.Accept = string.Join(",", requestToBroker.AcceptTypes);
httpRequest.ContentType = requestToBroker.ContentType;
httpRequest.Method = requestToBroker.HttpMethod;
if (requestToBroker.UrlReferrer != null)
{
httpRequest.Referer = requestToBroker.UrlReferrer.AbsoluteUri;
}
httpRequest.UserAgent = requestToBroker.UserAgent;
/* This is a performance change which I like.
* If this is not explicitly set to null, the CLR will do a registry hit for each request to use the default proxy.
*/
httpRequest.Proxy = null;
if (requestToBroker.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
{
BridgeAndCloseStreams(requestToBroker.InputStream, httpRequest.GetRequestStream());
}
return httpRequest;
}
/// <summary>
/// Convert System.Net.Cookie into System.Web.HttpCookie
/// </summary>
private static HttpCookie CookieToHttpCookie(Cookie cookie)
{
HttpCookie httpCookie = new HttpCookie(cookie.Name);
foreach (string value in cookie.Value.Split('&'))
{
string[] val = value.Split('=');
httpCookie.Values.Add(val[0], val[1]);
}
httpCookie.Domain = cookie.Domain;
httpCookie.Expires = cookie.Expires;
httpCookie.HttpOnly = cookie.HttpOnly;
httpCookie.Path = cookie.Path;
httpCookie.Secure = cookie.Secure;
return httpCookie;
}
/// <summary>
/// Reads from stream into the to stream
/// </summary>
private static void BridgeAndCloseStreams(Stream from, Stream to)
{
try
{
int read;
do
{
read = from.ReadByte();
if (read != -1)
{
to.WriteByte((byte)read);
}
}
while (read != -1);
}
finally
{
from.Close();
to.Close();
}
}
}
}
It worked like Kevin said.
I'm using a static method to retrieve the HttpContext.Current.Request, and so always have a HttpRequest object for use when needed.
Here in Class Helper
public static HttpRequest GetRequest()
{
return HttpContext.Current.Request;
}
Here in Controller
if (AcessoModel.UsuarioLogado(Helper.GetRequest()))
Here in View
bool bUserLogado = ProjectNamespace.Models.AcessoModel.UsuarioLogado(
ProjectNamespace.Models.Helper.GetRequest()
);
if (bUserLogado == false) { Response.Redirect("/"); }
My Method UsuarioLogado
public static bool UsuarioLogado(HttpRequest Request)

Resources