首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HttpPost用于操作(保存)以404结尾-错误

HttpPost用于操作(保存)以404结尾-错误
EN

Stack Overflow用户
提问于 2014-06-24 09:21:39
回答 1查看 642关注 0票数 1

我有一个C#网站和Web的结合。在Web部分中,我有四个控制器(用于产品、类别、用户和订单)。MVC控制器默认使用的普通GetAllProducts、GetProduct等都很好,但是现在我想为HttpPost (保存订单)提供一个操作方法。

下面是我之前想要添加的保存操作(PS:在底部,您可以看到我试图使保存操作工作的内容):

WebApiConfig.cs:

代码语言:javascript
复制
using WebMatrix.WebData;
using System.Web.Security;

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Only allow JSON response format
        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        // Default API HttpRoute
        // Call examples: "/api/products" or "/api/products/2"
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // Configure the DependencyResolver and register all API-Controllers
        var container = new UnityContainer();
        container.RegisterType<IProductsRepository, ProductsRepository>(new HierarchicalLifetimeManager());
        container.RegisterType<ICategoriesRepository, CategoriesRepository>(new HierarchicalLifetimeManager());
        container.RegisterType<IOrdersRepository, OrdersRepository>(new HierarchicalLifetimeManager());
        container.RegisterType<IUsersRepository, UsersRepository>(new HierarchicalLifetimeManager());
        config.DependencyResolver = new ControllerDependencyFactory(container);

        // Some other stuff
        ...
    }
}

ControllerDependencyFactory.cs:

代码语言:javascript
复制
// This class uses the Inversion of Control (IoC) pattern, which creates
// instances of objects for you. All Web API Objects (API Controllers) are
// registered with this IDependencyResolver, so with a GET-request to the API
// this class will automatically create the correct instance of that API Controller.
public class ControllerDependencyFactory : IDependencyResolver
{
    protected IUnityContainer container;

    public ControllerDependencyFactory(IUnityContainer container)
    {
        if (container == null)
            throw new ArgumentNullException("container");

        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            // WARNING: Do not throw any errors, we need to return null here
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            // WARNING: Do not throw any errors, we need to return an empty List here
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new ControllerDependencyFactory(child);
    }

    public void Dispose()
    {
        container.Dispose();
    }
}

IProductsRepository.cs:

代码语言:javascript
复制
public interface IProductsRepository
{
    IEnumerable<ProductsAPI> GetAll();
    ProductsAPI Get(int id);
    // No need for an Add, Update or Remove
}

ProductsRepository.cs:

代码语言:javascript
复制
public class ProductsRepository : IProductsRepository
{
    private List<ProductsAPI> products;
    private ProductController controller;

    public ProductsRepository()
    {
        // Get the Controller-instance from the RouteMaps instead of creating a new instance here
        getControllerInstance();
        products = controller.ListAll();
    }

    public IEnumerable<ProductsAPI> GetAll()
    {
        return products;
    }

    public ProductsAPI Get(int id)
    {
        return products.Find(p => p.ProductId == id);
    }

    // No need for an Add, Update or Remove

    private void getControllerInstance()
    {
        var url = "~/product";
        // Original path is stored and will be rewritten in the end
        var httpContext = new HttpContextWrapper(HttpContext.Current);
        string originalPath = httpContext.Request.Path;

        try
        {
            // Fake a request to the supplied URL into the routing system
            httpContext.RewritePath(url);
            RouteData urlRouteData = RouteTable.Routes.GetRouteData(httpContext);

            // If the route data was not found (e.g url leads to another site) then authorization is denied.
            // If you want to have a navigation to a different site, don't use AuthorizationMenu
            if (urlRouteData != null)
            {
                string controllerName = urlRouteData.Values["controller"].ToString();

                // Get an instance of the controller that would handle this route
                var requestContext = new RequestContext(httpContext, urlRouteData);
                var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
                var controllerbase = (ControllerBase)controllerFactory.CreateController(requestContext, controllerName);
                controller = (ProductController)controllerbase;
            }
        }
        finally
        {
            // Reset our request path.
            httpContext.RewritePath(originalPath);
        }
    }

ProductsController.cs (仅由Web使用):

代码语言:javascript
复制
public class ProductsController : ApiController
{
    private IProductsRepository repository;

    public ProductsController()
    {
        repository = new ProductsRepository();
    }

    // GET /api/products
    public IEnumerable<ProductsAPI> GetAllProducts()
    {
        if(LockAPI.loggedIn)
            return repository.GetAll();

        return null;
    }

    // GET /api/products/5
    public ProductsAPI GetProduct(int id)
    {
        if (LockAPI.loggedIn)
        {
            ProductsAPI item = repository.Get(id);
            if (item == null)
                throw new HttpResponseException(HttpStatusCode.NotFound);
            return item;
        }

        return null;
    }
}

ProductController.cs (网站也使用):

代码语言:javascript
复制
public class ProductController : Controller
{
    private IMyDatabase db;

    public ProductController()
    {
        this.db = new MyDatabase();
    }

    // Some methods used by the Website part
    ...

    // List all products
    // This method is only used for the Web API part of this project, that will be used in the Mobile App
    // Return: actual List of Products, instead of JSON format
    public List<ProductsAPI> ListAll()
    {
        List<ProductsAPI> products = db.Products
            .Where(p => p.Visible == true)
            .OrderBy(p => p.Name)
            .Select(p => new ProductsAPI()
            {
                ProductId = p.ProductId,
                Name = p.Name,
                Price = p.Price,
                CategoryId = p.CategoryId,
            })
            .ToList();

        return products;
    }
}

ProductsAPI.cs:

代码语言:javascript
复制
public class ProductsAPI
{
    // The Products Fields we use in the Mobile App
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
}

这就是我所拥有的,我添加/更改了以下内容:

在WebApiConfig.cs中改变了:

代码语言:javascript
复制
// Default API HttpRoute
// Call examples: "/api/products" or "/api/products/2"
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { id = @"^\d+$" } // Only integers
);

至:

代码语言:javascript
复制
// Default API HttpRoute
// Call example: "/api/orders"
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}"
);
// API HttpRoute with ID
// Call example: "api/orders/2"
config.Routes.MapHttpRoute(
    name: "ApiWithID",
    routeTemplate: "api/{controller}/{id}",
    defaults: null,
    constraints: new { id = @"^\d+$" } // Only integers
);
// Api HttpRoute with Action
// Call example: "api/orders/save"
config.Routes.MapHttpRoute(
    name: "ApiWithAction",
    routeTemplate: "api/{controller}/{action}"
);

在OrdersController.cs (仅由Web使用)中添加了以下方法:

代码语言:javascript
复制
// POST /api/orders/save
[HttpPost]
[ActionName("save")]
public Boolean SaveOrder([FromUri] string UniqueID, [FromBody] dynamic Data)
{
    if(LockAPI.loggedIn)
    {
        // Convert JSON body to a Key-Value Dictionary
        Dictionary<string, object> data = JsonConvert.DeserializeObject<Dictionary<string, object>>(Convert.ToString(Data));

        // Convert data-objects
        ...

        // Use this converted data to save the Orders
        // and return true or false based on the result
        ...
    }

    return false;
}

现在我使用的是FireFox RESTClient:

方法:POST - http://localhost/api/orders/save

身体:"productIds": "[20, 25]", "prices": "[0.40, 7.40]", dateInTicks: "1402444800"

并得到以下答复:

代码语言:javascript
复制
Status Code: 404 Not Found
Cache-Control: no-cache
Connection: Close
Content-Length: 212
Content-Type: application/json; charset=utf-8
Date: Tue, 24 Jun 2014 08:45:10 GMT
Expires: -1
Pragma: no-cache
Server: ASP.NET Development Server/11.0.0.0
X-AspNet-Version: 4.0.30319

{
    "$id": "1",
    "Message": "No HTTP resource was found that matches the request URI 'http://localhost/api/orders/save'.",
    "MessageDetail": "No action was found on the controller 'Orders' that matches the request."
}

如何使HttpPost到C# mvc Web以处理操作?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-25 07:14:03

我第一次尝试了另一个MapHttpRoute:

代码语言:javascript
复制
// Default API HttpRoute
// Call examples: "/api/products/all" or "/api/products/one/2" or "/api/orders/save" or "/api/test/enable"
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

以及对Web控制器的一个小改动:

  • [ActionName("all")]添加到GetAll方法(这将/api/products更改为/api/products/all)
  • [ActionName("one")]添加到Get-方法(这将/api/products/2更改为/api/products/one/2)

并将OrdersController中的SaveOrder-方法更改为:

代码语言:javascript
复制
public Boolean SaveOrder([FromBody] string productIds, [FromBody] string newPrices, [FromBody] string dateInTicks)
{
    ...
}

当我再次使用FireFox‘RESTClient做同样的文章时,我遇到了一个500错误:

代码语言:javascript
复制
Status Code: 500 Internal Server Error
Cache-Control: no-cache
Connection: Close
Content-Length: 226
Content-Type: application/json; charset=utf-8
Date: Tue, 24 Jun 2014 14:33:51 GMT
Expires: -1
Pragma: no-cache
Server: ASP.NET Development Server/11.0.0.0
X-AspNet-Version: 4.0.30319

{
    "$id": "1",
    "Message": "An error has occurred.",
    "ExceptionMessage": "Can't bind multiple parameters ('productIds' and 'dateInTicks') to the request's content.",
    "ExceptionType": "System.InvalidOperationException",
    "StackTrace": null
}

这更有道理。

然后,我将Save方法更改为:

代码语言:javascript
复制
public Boolean SaveOrder([FromBody] JObject jsonData)
{
    // TODO: Seperate the JSON Data

    // Rest the same as before
    ...
}

现在它转到方法上,我可以通过它进行调试。

现在,我在将正文中的JSON转换为实际分离的数据时仍然有问题,但这是另一个问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24382895

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档