Spring 3.0 RESTful Controller Fails on Redirect

2019-08-19 01:19发布

问题:

I am setting up a simple RESTful controller for a Todo resource with an XML representation. It all works great - until I try to redirect. For example, when I POST a new Todo and attempt to redirect to its new URL (for example /todos/5, I get the following error:

Error 500 Unable to locate object to be marshalled in model: {}

I do know the POST worked because I can manually go to the new URL (/todos/5) and see the newly created resource. Its only when trying to redirect that I get the failure. I know in my example I could just return the newly created Todo object, but I have other cases where a redirect makes sense. The error looks like a marshaling problem, but like I said, it only rears itself when I add redirects to my RESTful methods, and does not occur if manually hitting the URL I am redirecting to.

A snippet of the code:

@Controller
@RequestMapping("/todos")
public class TodoController {

    @RequestMapping(value="/{id}", method=GET)
    public Todo getTodo(@PathVariable long id) {
        return todoRepository.findById(id);
    }

    @RequestMapping(method=POST)
    public String newTodo(@RequestBody Todo todo) {
        todoRepository.save(todo); // generates and sets the ID on the todo object
        return "redirect:/todos/" + todo.getId();
    }

    ... more methods ...

    public void setTodoRepository(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    private TodoRepository todoRepository;
}

Can you spot what I am missing? I am suspecting it may have something to do with returning a redirect string - perhaps instead of it triggering a redirect it is actually being passed to the XML marshaling view used by my view resolver (not shown - but typical of all the online examples), and JAXB (the configured OXM tool) doesn't know what to do with it. Just a guess...

Thanks in advance.

回答1:

This happend because redirect: prefix is handled by InternalResourceViewResolver (actually, by UrlBasedViewResolver). So, if you don't have InternalResourceViewResolver or your request doesn't get into it during view resolution process, redirect is not handled.

To solve it, you can either return a RedirectView from your controller method, or add a custom view resolver for handling redirects:

public class RedirectViewResolver implements ViewResolver, Ordered {
    private int order = Integer.MIN_VALUE;

    public View resolveViewName(String viewName, Locale arg1) throws Exception {
        if (viewName.startsWith(UrlBasedViewResolver.REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(UrlBasedViewResolver.REDIRECT_URL_PREFIX.length());
            return new RedirectView(redirectUrl, true);
        }
        return null;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}