ServiceStack Razor Response Filter - asp.net

I Write a ServiceStack Razor Page named 'default.cshtml'. I want to add a global response filter on it running, but it not work right. how to fixed it?
private static void AddFilters(IAppHost appHost)
{
appHost.GlobalResponseFilters.Add((req, res, dto) =>
{
res.AddHeader("X-Powered-By", "mylvgth");
});
}

GlobalResponseFilters are for requests that populate Request DTOs and are executed by Services. For other requests you can use PreRequestFilters which is executed at the start of a Request.
There are no Response filters for Razor pages as you can’t add Headers to a Request after its already written to the Response, only for “View Pages” which call Services first where the Response filter is executed before the page is rendered.

Related

Handle Unauthorized Request and Return Status Code 404

I am developing a standalone .Net Core API targeting framework .Net Core 2.2.The authentication scheme is JWTBearerTokens connecting to our ADFS Identify server.
When I call an API endpoing decorated with the [Authorize] attribute I am getting a 401 Unauthorized response, which is expected and default behaviour.
What I want to do next is instead of having that same call return a 401, I would like to return the status code to be 404. (I don't want to get into great details of why 404. Simply, I do not want to expose that the endpoint exists if a valid token is not included in request)
In previous .Net Framework WebAPI you could create your own attribute and override the HandleUnauthorizedRequest method and return the status code you want.
I have reviewed the documentation on policy-based authorization, but have not tried the sample or tried implementing it. The policy handler looks more to do with handling (return success or fail) if a policy is not fulfilled. I do not see anywhere where you can return a different status code on failure. So that only would make sense if I start checking against actual Policies.
Any insights?
Returning 404 instead of 401 is bad practice(as mentioned in the comments by #Chris Pratt) and must be avoided. Consider these cases,
You're leaving the project to someone else and they can't figure why 404 is returned
A 404 is returned when you call the homepage/Index page. Poor ideology.
Later on in the project, you decide to allow post requests without authentication. So on and so forth.
Anyways, as part of the community, I'll give you the answer...
Add this to your global.asax
void Application_EndRequest(object source, System.EventArgs args)
{
if (Response.StatusCode == 401)
{
Response.ClearContent();
Response.RedirectToRoute("ErrorH", (RouteTable.Routes["ErrorH"] as Route).Defaults);
}
}
And in routeConfig, create a route for your errorHandler :
routes.MapRoute(
"ErrorH",
"Error/{action}/{errMsg}",
new { controller = "CustomController", action = "Change401To404", errMsg = UrlParameter.Optional }
);
And in your custom controller :
public class CustomController : Controller //or Base
{
public ActionResult Change401To404(){
//Do whatever you want
}
}
PS: This is not the only way, there are many other ways to do it. But at least in this method, you can differentiate real 404 responses from 401 responses.

ASP.NET MVC, Is it possible to code a Response.AppendToLog in one place which will act on every Request?

I am using ASP.NET 4.7 and MVC5 with C# with IIS Express locally and published to Azure App Services.
I want to add something like:
Response.AppendToLog("XXXXX Original IP = 12.12.12.12 XXXXX");
Which adds an Original IP address to the request string in the "request" column in the web server log.
If I add this to a specific "get" Action this works fine. However I do not want to add this code to every Action. Is it possible to place it more centrally such that it gets executed on every "Get" / Request. This may be a simple question, but the answer alludes me at present
Thanks for any wisdom.
EDIT: Is this via Custom Action Filters?
if (filterContext.HttpContext.Request.HttpMethod=="GET")
{
Response.AppendToLog... //I know this will not work as Response not known.
}
You almost know the answer. Try handling OnActionExecuted that gets you the Response.
public class CustomActionFilter : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Request.Method == HttpMethods.Get)
{
}
}
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
var response = context.HttpContext.Response;
}
}
My solution to write out text:
filterContext.RequestContext.HttpContext.Response.AppendToLog("OrigIP");

Issue with RedirectAction

I have a FormController with Index action and SimpleController with CorticonIndex action.
I am redirecting to CorticonIndex from Index action. My problem is, I have put a breakpoint at return RedirectToAction() and CorticonIndex().
So,Only for the first time I can see the execution by F11 but for the second time controller is not going to CorticonIndex().
How the RedirectToAction() will work?
Is it only one time execution or can we execute multiple time??
FormController
[HttpPost]
public ActionResult Index(FormCollection formCollection)
{
return RedirectToAction("CorticonIndex", " SimpleController");
}
SimpleController
public ActionResult CorticonIndex()
{
var viewModel = this.Model.GetViewModel(payLoad);
return View(CorticonResponseModel.viewName, viewModel);
}
How the RedirectToAction() will work?
It sends an HTTP 302 response to the browser of the web site along with a Location header. What the browser does with that is up to the browser. Normally, it will use the Location header to submit another request to your server. But, as this is entirely out of the application's control, there are no guarantees.
But in this case you are initially calling the server using an HTTP POST. If your browser reloads the page using an HTTP GET, it will not hit this same action method. If you have an HTTP GET action method named Index, it will call that one instead.
NOTE: FYI - if you remove the HttpPost attribute, your action method will respond to both GET and POST. But that is probably not the solution to your issue, as it is normal to have a separate action method in each case.

How to pass new header to sendRedirect

I feel like this should be easy. I have an app where all I am trying to do is have a form page (index.jsp) that calls a servlet (CheckInfo.java) which sets a new header (myHeader) and redirects the user to another page (redirect.jsp). All of these files are on the same server. The index.jsp is sending the request just fine and CheckInfo is processing and redirecting, but myHeader is not showing up on redirect.jsp. I've read several posts talking about response.sendRedirect sends a 302 which doesn't pass headers and that I should use RequestDispatcher, but nothing seems to work. Is there no way to send headers from a servlet to a jsp?
Here is the servlet code:
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
I have also tried this:
response.setHeader("myHeader", "hey there");
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
view.forward(request, response);
And I have this in redirect.jsp:
System.out.println(request.getHeader("myHeader"));
This does not print anything.
If the answer to my question is no... then I would settle for a way to set the header once I got back to the jsp. My reverse proxy is looking for a specific header to determine whether or not to perform an action. Obviously I tried response.addHeader() on redirect.jsp, but the page has already loaded at that point so that just made me feel dumb.
response.setHeader("myHeader", "hey there");
response.sendRedirect("redirect.jsp");
You are adding it as response header and it is 302 response. Browser on seeing a 302 response will just look for Location header and fire a new request to this location. Custom headers in the response are untouched whereas you are expecting these custom response headers to be included in the request (to new redirect location) which is not being sent.
Solution:-
1. you can use request dispatcher and forward the request instead of external redirect. And you need to use request attributes here.
2. you can call submit form using an ajax request may be jquery like and handle the response manually(for 302 response) but would not suggest you to use this approach as it is not a cleaner and intuitive approach. Just mentioning so that you know there are other ways to achieve this.
The problem is that the redirect() method of the response initiates a new request altogether, thereby loosing the attributes that were set before redirecting. Luckily there is a fluent way of solving the problem still. See below
response.setHeader("myHeader", "hey there");
request.getRequestDispatcher("redirect.jsp").forward(request, response);
Then in your destination you can do response.getHeaders("myHeader")
I have tested the code.
I hope it's clear that in case of asking the client to redirect to another URL - the browser shall not honor the cookies.
However, the 2nd method - where server forwards the request is feasible. The main mistake appears to be in mutating the response while we are supposed to change the request.
Then again, one cannot directly mutate a HttpServletRequest object. Here is one way to do so:
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request){
public String getHeader(String name) {
String value = super.getHeader(name);
if(Strings.isNullOrEmpty(value)) {
...
value = myNewHeader;
}
return value;
}
public Enumeration<String> getHeaders(String name) {
List<String> values = Collections.list(super.getHeaders(name));
if(values.size()==0) {
...
values.add(myNewHeader);
}
return Collections.enumeration(values);
}
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.add(myNewHeaderName);
...
return Collections.enumeration(names);
}
}
Followed by:
RequestDispatcher view = request.getRequestDispatcher("redirect.jsp");
// OR (If you can get servletContext)
RequestDispatcher view = servletContext.getRequestDispatcher("redirect.jsp");
view.forward(requestWrapper, response);
Reference:
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequestWrapper.html
For the headers case - getHeader(), getHeaders() and getHeaderNames() fn in the reqWrapper obj need Overriding.
Similarly you can override cookies and params.
See also: Modify request parameter with servlet filter
NOTE: It might not be possible to forward a req to an endpoint which expects a different MIME type.
A client side redirect creates a new HTTP request/response pair.
This link may help you more on debugging perspective -
Sending Custom headers

Spring MVC binding request parameters

I wrote a spring-mvc controller method to get an array of values in the request parameter.The method looks like below
/**
Trying to get the value for request param foo which passes multiple values
**/
#RequestMapping(method=RequestMethod.GET)
public void performActionXX(HttpServletRequest request,
HttpServletResponse response,
#RequestParam("foo") String[] foo) {
......
......
}
The above method works fine when the request url is in below format
...?foo=1234&foo=0987&foo=5674.
However when the request url is in below format the server returns 400 error
...?foo[0]=1234&foo[1]=0987&foo[2]=5674
Any idea how to fix the method to cater to the second format request url?
This is not possible with #RequestParam. What you can do is implement and register your own HandlerMethodArgumentResolver to perform to resolve request parameters like
...?foo[0]=1234&foo[1]=0987&foo[2]=5674
into an array. You can always checkout the code of RequestParamMethodArgumentResolver to see how Spring does it.
Note that I recommend you change how the client creates the URL.
The server is supposed to define an API and the client is meant to follow it, that's why we have the 400 Bad Request status code.
I resolved this issue using the request.getParameterMap().Below is code.
Map<String,String> parameterMap= request.getParameterMap();
for(String key :parameterMap.keySet()){
if(key.startsWith("nameEntry")){
nameEntryLst.add(request.getParameter(key));
}
}

Resources