In my ASP.NET MVC 2 application I use HandleErrorAttribute to display a custom error page in case of unhandled exceptions, and it works perfectly unless the exception happens in an action called by Ajax.ActionLink. In this case nothing happens. Is it possible to use HandleErrorAttribute to update the target element with the contents of an "Error.ascx" partial view?
To achieve this you could write a custom action filter:
public class AjaxAwareHandleErrorAttribute : HandleErrorAttribute
{
public string PartialViewName { get; set; }
public override void OnException(ExceptionContext filterContext)
{
// Execute the normal exception handling routine
base.OnException(filterContext);
// Verify if AJAX request
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// Use partial view in case of AJAX request
var result = new PartialViewResult();
result.ViewName = PartialViewName;
filterContext.Result = result;
}
}
}
And then specify the partial view to be used:
[AjaxAwareHandleError(PartialViewName = "~/views/shared/error.ascx")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult SomeAction()
{
throw new Exception("shouldn't have called me");
}
}
And finally in your view assuming you have the following link:
<%= Ajax.ActionLink("some text", "someAction", new AjaxOptions {
UpdateTargetId = "result", OnFailure = "handleFailure" }) %>
You could make the handleFailure function to update the proper div:
<script type="text/javascript">
function handleFailure(xhr) {
// get the error text returned by the partial
var error = xhr.get_response().get_responseData();
// place the error text somewhere in the DOM
document.getElementById('error').innerHTML = error;
}
</script>
Related
I've a route like Admin/Vendor in my MVC application . Without changing this route I need to point this same route to another method say CustomAdmin/CustomVendor.
I tried attribute routing but no luck . Is there any way to do this. My current code is given below
Original Method:
public class AdminController
{
public ActionResult Vendor()
{
return View();
}
}
Custom Method:
public class CustomAdminController
{
[Route("Admin/Vendor")]
public ActionResult CustomVendor()
{
return View();
}
}
As you're developing a plugin. You have to add your custom route to the RouteProvider.
In default nopCommerce AdminController and Vendor doesn't exists, so I assume that you're trying to override vendor list method of admin.
Which looks like:
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
var route = routes.MapRoute("Plugin.GroupName.PluginName.CustomVendor",
"Admin/Vendor/List",
new { controller = "CustomAdminController", action = "CustomVendor", orderIds = UrlParameter.Optional, area = "Admin" },
new[] { "Nop.Plugin.GroupName.PluginName.Controllers" });
route.DataTokens.Add("area", "admin");
routes.Remove(route);
routes.Insert(0, route);
}
public int Priority
{
get
{
return 100; // route priority
}
}
}
Side Note: GroupName and PluginName should be your plugin group name and plugin name.
Hope this helps !
On your plugin which class implements the interface IRouteProvider, you can easily override the route there.
Likewise I have a class named RouteProvider in my plugin, So I have Implemented the abstract function RegisterRoutes and simply it can be overrided by
routes.MapRoute("Plugin.Promotion.Combo.SaveGeneralSettings",
"Admin/Vendor",
new { controller = "CustomAdmin", action = "CustomVendor", },
new[] { "Nop.Plugin.Promotion.Combo.Controllers" }
);
Here Plugin.Promotion.Combo must be replaced by your plugin directory.And using SaveGeneralSettings or any things you want to use that will be your route url
To stay basic I would like to create a bookmark app
I have a simple bookmarklet
javascript:location.href='http://zas.dev/add?url='+encodeURIComponent(location.href)
I created a rest controller
<?php
use zas\Repositories\DbLinkRepository;
class LinksController extends BaseController {
protected $link;
function __construct(DbLinkRepository $link) {
$this->link=$link;
// ...
//$this->beforeFilter('auth.basic', array('except' => array('index', 'show', 'store')));
// ...
}
public function index()
{
//return Redirect::to('home');
}
public function create()
{
}
public function store()
{
return 'hello';
//$this->link->addLink(Input::get('url'));
//return Redirect::to(Input::get('url'));
}
public function show($id)
{
//$url = $this->link->getUrl($id);
//return Redirect::to($url);
}
public function edit($id)
{
}
public function update($id){
}
public function destroy($id){
}
}
in the routes.php, I created a ressource
Route::resource('links','LinksController');
and as I want to redirect /add to the store method I added
Route::get('/add',function(){
return Redirect::action('LinksController#store');
});
but it never display the hello message, in place it redirects me to
http://zas.dev/links
I also tried with
return Redirect::route('links.store');
without much success
thanks for your help
Ok I now get what you are trying to do. This will work:
Route::get('add', 'LinksController#store');
Remove:
Route::resource('links','LinksController');
and remove:
Route::get('/add',function(){
return Redirect::action('LinksController#store');
});
Sorry it took so long!
The problem is that once you Redirect::, you loose all the Input values, so you should manually give them to your controller when you do the redirect, like so :
Redirect::route('links.store', ["url" => Input::get("url")]);
Finally add an $url parameter to your store method to receive the value we give it in the previous method, like this :
public function store($url) {
$this->link->addLink($url);
return Redirect::to($url);
}
I'm writing REST api using Zend Framework 1.12. I want to check "Authorization" header in controller plugin.
I put code in the preDispatch action of the plugin
$authorizationHeader = $request->getHeader('Authorization');
if(empty($authorizationHeader)) {
$this->getResponse()->setHttpResponseCode(400);
$this->getResponse()->setBody('Hello');
die(); //It doesn't work
}
The problem is that after it controller's action is still being called. I tried 'die()', 'exit'. My question is how to return response from plugin and do not call controller's action.
Did a similar REST API with Zend several weeks ago with this approach:
Class Vars/Consts:
protected $_hasError = false;
const HEADER_APIKEY = 'Authorization';
My preDispatch:
public function preDispatch()
{
$this->_apiKey = ($this->getRequest()->getHeader(self::HEADER_APIKEY) ? $this->getRequest()->getHeader(self::HEADER_APIKEY) : null);
if (empty($this->_apiKey)) {
return $this->setError(sprintf('Authentication required!'), 401);
}
[...]
}
My custom setError Function:
private function setError($msg, $code) {
$this->getResponse()->setHttpResponseCode($code);
$this->view->error = array('code' => $code, 'message' => $msg);
$this->_hasError = true;
return false;
}
Then simply check if a error has been set inside your functions:
public function yourAction()
{
if(!$this->_hasError) {
//do stuff
}
}
If you're using contextSwitch and JSON, then your array with errors will be automatically returned & displayed, if an error occours:
public function init()
{
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$this->_helper->contextSwitch()->initContext('json');
[...]
}
Hope this helps
Since checking headers is typically a low level request operation, you could do the header verification and then throw an exception if not valid in dispatchLoopStartup of the plugin. Then in your error controller, return the appropriate response. This would prevent the action from being dispatched/run and could be applied to any controller/action without modifying any controller code.
Controller plugin:
class AuthHeader extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(\Zend_Controller_Request_Abstract $request)
{
// Validate the header.
$authorizationHeader = $request->getHeader('Authorization');
if ($invalid) {
throw new Zend_Exception($error_message, $error_code);
}
}
}
Error handler:
class ErrorController extends Zend_Controller_Action
{
public function init()
{
// Enable JSON output for API originating errors.
if ($this->isApiRequest($this->getRequest())) {
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('error', 'json')
->setAutoJsonSerialization(true)
->initContext('json');
}
}
public function errorAction()
{
// Handle authorization header errors
// ...
// Handle errors
// ...
}
public function isApiRequest($request)
{
// Determine if request is an API request.
// ...
}
}
I am Using the Zend Framework.
As a design pattern i am using the state design pattern.
Now as you may know, Zend Framework let's you create custom controllers, which can be used to respond to Ajax requests.
In my example i have the following ajax request
function getResponse(name){
$.ajax({
dataType: 'json',
data: {button: name},
url: 'motor/ajaxtest',
type: 'post',
success: function(response)
{
}
});
}
The function getResponse is called every time a specific button is pressed.
public function ajaxtestAction()
{
$input_in = $this->getRequest()->getParam('button');
$Lok = new Lok();
$this->_helper->viewRenderer->setNoRender();
$text = array($Lok->getMotorState());
$phpNative = Zend_Json::encode($text);
echo $phpNative;
}
The Code above is my custom response to the ajax request. I want to pass on the name of the pressed button to $Lok = new Lok(); so i can use it in the "Lok" model Class without creating a new instance of The controller in the "Lok" class
Is there anyone who might be able to help me ?
EDIT-----------------------------------
Here's my Controller :
class MotorController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
}
public function ajaxtestAction()
{
$input_in = array($this->getRequest()->getParam('button'));
$phpNativ1 = Zend_Json::encode($input_in);
echo $phpNativ1;
$Lok = new Lok();
echo $input_in;
$this->_helper->viewRenderer->setNoRender();
$text = array($Lok->getMotorState());
$phpNative = Zend_Json::encode($text);
echo $phpNative;
}
}
Here are my Jquery functions :
$(document).ready(function(){
$("p").click(function(){
$(this).hide();
$("input[name=State]").val('Forwards');
});
function getResponse(name){
$.ajax({
dataType: 'json',
data: {button: name},
url: 'motor/ajaxtest',
type: 'post',
success: function(response)
{
}
});
}
$("button[name=on]").click(function() {
var d_response = getResponse('on');
});
});
And this is my Lok.php file :
class Lok
{
private $newMotor;
private $newTimer;
private $newSpeaker;
private $mySession;
private $motorState;
private $input;
public function __construct()
{
//Method instances
$newMotor = new Motor();
$newTimer = new Timer();
$newSpeaker = new Speaker();
$this->motorState = $newMotor->getMotorState();
// Declaring the Session
$mySession = new Zend_Session_Namespace();
$mySession->s_motorState = $this->motorState;
}
public function __get($mySession)
{
return $this->mySession;
}
public function __set($motorState, $mySession)
{
$this->$mySession->s_motorState = $motorState;
}
public function getMotorState()
{
return $this->motorState;
}
public function playSound($soundNumber)
{
echo "Playing sound";
}
public function resetTimer()
{
echo "Resetting timer";
}
public function setInput($input_in)
{
$this->input=$input_in;
}
}
As i've stated previously you should get the button name by calling the requests post data, this is done by $postData = $this->getRequest()->getPost()
Then, to get the output into your Model, inside your model class you would create a property as well as setter and getter method for it.
class Lok {
protected $button;
public function setButton($btn){}
public function getButton(){}
}
And then it becomes as easy as doing something like
$lokModel->setButton($postData['button'])
First of all, thanks for posting your solution.
I tried to implement your solution but unfortunaly it didnt work.
So after a good night sleep, i looked at the problem again. I think the problem is, that
$postData = $this->getRequest()->getPost() or $postData = $this->getRequest()->getParam('button')
is executed in the response itelfe.
<pre>string(2) "sr"
</pre>["Not Moving"]
This is what the JSON response looks like in The Google Chrom debugger. If you'r familliar with this Google Chrome debugger you know what i mean.
Now the button name that i want is in between the " tags the only problem is, getting it out of there. and being able to use it before the response is triggerd. I also tried getPost() and getParam('button') in the init() and indexAktion() Methods in the Controller but it still didn't work
public function init()
{
postData = $this->getRequest()->getPost()
$lokModel->setButton($postData['button'])
}
public function indexAction()
{
postData = $this->getRequest()->getPost()
$lokModel->setButton($postData['button'])
}
Any other ideas ?
I have an action bean in my stripes application. The default handler/method will display a list of data, a list of all my MarketResearch objects
On my JSP, I can click on one to view its details, this takes me to a different JSP with a pre-populated form based on the particular MarketResearch object that you selected.
I have another method on my action bean which is mapped to the save submit button, this takes in what is on the amended form, and persists it. After this has taken place, I want it to redirect back to the form, rather than to the listing (default handler) action, is this possible?
My action is as follows :
public class MarketResearchAction extends BaseAction
{
#SpringBean
ClientService clientService;
private static final String VIEW = "/jsp/marketResearch.jsp";
private Client client;
private Client clientBeforeChanges;
public Client getClient()
{
return client;
}
public void setClient(Client client)
{
this.client = client;
}
#DefaultHandler
public Resolution viewAll()
{
return new ForwardResolution(VIEW);
}
public Resolution viewClientMarketResearch()
{
if (client.getSector().equals("Education"))
{
return new ForwardResolution("/jsp/marketResearchEducation.jsp");
} else if (client.getSector().equals("Local Government"))
{
return new ForwardResolution("/jsp/marketResearchLocalGovernment.jsp");
} else if (client.getSector().equals("Housing Association"))
{
return new ForwardResolution("/jsp/marketResearchHousing.jsp");
}
return new ForwardResolution("/jsp/viewClientMarketResearch.jsp");
}
public Resolution save()
{
clientBeforeChanges = clientService.getClientById(client.getId());
clientService.persistClient(client);
getContext().getMessages().add(new SimpleMessage("{0} updated", client.getName()));
return new RedirectResolution("/MarketResearch.action").flash(this);
}
public Client getClientBeforeChanges()
{
return clientBeforeChanges;
}
public void setClientBeforeChanges(Client clientBeforeChanges)
{
this.clientBeforeChanges = clientBeforeChanges;
}
public ClientService getClientService()
{
return clientService;
}
public void setClientService(ClientService clientService)
{
this.clientService = clientService;
}
}
Is it possible? Or am I approaching the situation from a bad angle and should re-factor?
Thanks
Yes. You could return a RedirectResolution to the form jsp. If you're having difficulty with the parameters, if you have them in the save() method, you could do like so:
return new RedirectResolution("/theJsp.jsp")
.addParameter("one", one)
.addParameter("two", two)
.addParameter("three", three)
.flash(this);
If you don't have the params that were passed to the form, you'll have to keep them going somehow. You could pass the MarketResearch object through the form so you'd have it there.
<stripes:hidden name="marketResearch" value="${ActionBean.marketResearch}"/>
And add the requisite instance variable/getter/setter on your MarketResearchActionBean.